【原】react+redux實戰

摘要:由於最近搞懂了redux的異步操做,因此以爲能夠用react+redux來作一個小小的項目了,以此來加深一下印象。切記,是小小的項目,因此項目確定是比較簡單的啦,哈哈。javascript

 

項目效果圖如圖所示:(由於一張圖實現起來太長了 ,因此切成了兩半,略醜,罪過!!)css

 

 

項目的github地址:https://github.com/xianyulaodi/react-redux前端

 以前寫過一篇文章是redux的異步操做,本文只是將其應用到了實戰當中。java

 

用react+redux的步驟是,首先須要知道大概有哪些狀態,上圖中,有如下幾個狀態:node

 

同步狀態:排行版和規則頁切換的狀態react

異步狀態:熱氣球星榜和魔術帽星榜等四個tab切換的狀態,查看上週的狀態git

 

是的,不要搞那麼複雜,這個活動頁就這幾個狀態的,不過這樣對於入門頗有好處,由於比較好理解嘛github

talk is cheap,show me the code。json

來看咱們的 actionsredux

 

actions/index.js

 

// 因爲目前大多數瀏覽器原生還不支持它,建議你使用 isomorphic-fetch 庫:
// 每次使用 `fetch` 前都這樣調用一下
import fetch from 'isomorphic-fetch'
export const RECEIVE_POSTS = 'RECEIVE_POSTS';
export const IS_LASTWEEK = 'IS_LASTWEEK';
export const TAB_CHANGE = 'TAB_CHANGE';
export const GIFT_ID_CHOICE = 'GIFT_ID_CHOICE';

// 本案例的狀態有如下幾個:

// 一、上週和本週的切換  
// 二、點擊四個tab的切換
// 三、排行版和規則頁的切換

/*
  上週和本週的 action
  其中weekOffset有兩個值,本週爲0,上週爲1
*/
export function weekChoice(weekOffset) {
  return {
    type: IS_LASTWEEK,
    weekOffset
  }
}

/*
  熱氣球星榜和魔術帽星榜的action,值爲giftId giftId的值可爲401,402
  其中401是熱氣球,402是魔術帽
*/
export function giftIdChoice(giftId) {
  return {
    type: GIFT_ID_CHOICE,
    giftId
  }
}

// 排行版和規則頁切換
export function tabChange(tabId) {

  return {
     type: TAB_CHANGE, 
     tabId
  }

}

/*
  獲取數據成功的action,將全部的數據傳回去
  返回的狀態爲 posts
*/

function receivePosts(reddit, json) {
  return {
    type: RECEIVE_POSTS,
    posts:json
  }
}

/**
  請求的函數,傳值從這裏傳
**/
function fetchPosts(weekOffset,giftId) {
  return function (dispatch) {
      // data.weekOffset=weekOffset;  // 0 本週 1上週
      // data.giftId=giftId;   // 禮物id 401是熱氣球,402是魔術帽
    return fetch(`http://api.ys.m.yy.com/api/internal/gift/rank.json?data={"platform":1,"weekOffset":${weekOffset},"giftId":${giftId},"uid":0}`)
      .then(response => response.json())
      .then(json =>
        dispatch(receivePosts(weekOffset, json))
      )
  }
}

//獲取數據
export function fetchPostsIfNeeded(weekOffset,giftId) {

  return (dispatch, getState) => {

      return dispatch(fetchPosts(weekOffset,giftId))

    }
}

  這裏定義了幾個action,是根據上面說的同步和異步的狀態來定義的,

  定義了選擇是上週仍是本週的action  weekChoice,並返回一個weekOffset,這個值是要傳給請求的一個參數。0爲本週,1爲上週

  定義了是熱氣球仍是星球帽的action giftIdChoice,並返回giftId,這個也是傳給請求的一個參數。401爲熱氣球的,402爲星球帽

  定義了排行版和規則頁切換的action tabChange,並返回一個tabId, 0爲排行版,1爲規則頁。這個頁面的切換是同步的。

其餘的好比是fetch的請求的action能夠參考我以前寫一篇文章,地址爲:這裏是上一篇文章地址。

 

action的代碼很少,接下來講一說reducer的代碼:

 

reducers/index.js

import { combineReducers } from 'redux'
import {
  RECEIVE_POSTS,
  IS_LASTWEEK,
  GIFT_ID_CHOICE,
  TAB_CHANGE
} from '../actions'


function isNextWeek(state = 0, action) {
  switch (action.type) {
    case IS_LASTWEEK:
      return action.weekOffset  //這裏面的值是和action裏面的值對應的
    default:
      return state
  }
}

function tabIdState(state=0,action){
  switch(action.type){
    case TAB_CHANGE :
      return action.tabId
    default:
      return state
  }
}

function giftId(state = 401, action) {
  
  switch (action.type) {

    case GIFT_ID_CHOICE:
      return action.giftId  
       //這裏面的值是和action裏面的值對應的
    default:
      return state
  }
}

  /** Object.assign是ES6的一個語法。合併對象,將對象合併爲一個,先後相同的話,後者覆蓋強者。詳情能夠看這裏
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
  例如:
      var obj = { a: 1 };
      var copy = Object.assign({}, obj,{'b':2});
      console.log(copy); // { a: 1,b:2 }
      
      var obj = { a: 1 };
      var copy = Object.assign({}, obj,{'a':2});
      console.log(copy); // { a: 2}

  **/
function receiveData(state ={}, action) {
  switch (action.type) {
    case RECEIVE_POSTS:
      return Object.assign({}, state, {
        items: action.posts //請求獲得的數據都存在了這裏
      })
    default:
      return state
  }
}

// 將全部的reducer結合爲一個,傳給store
const rootReducer = combineReducers({
  receiveData,
  isNextWeek,
  giftId,
  tabIdState
})

export default rootReducer

    reducre也比較簡單,Action對象僅僅是描述了行爲的相關信息,至於如何經過特定的行爲來更新state,就須要看看Reducer了。

從上面的代碼能夠得知,reducer有一下幾點特性,

  一、它是一個純函數。

  二、它接收兩個參數,一個是舊的狀態previousState和一個Action對象。

  三、它返回一個新的狀態NewState。 你能夠返回任何的東西,對象、數組、字符串等等等等

  好比個人代碼中,當接收到 IS_LASTWEEK狀態描述,咱們就返回一個新的 weekOffset  ,當接收到 RECEIVE_POSTS的狀態描述,咱們就返回一個對象,將數據返回到items對象裏面。等等等等。反正經過reducer,當你接收到某個狀態描述時,能夠返回任何東西,並且state須要的話能夠給一些默認值。

  其實這些拆分開來的reducer的函數名,纔是供View使用的狀態值。好比我reducer裏的那些純函數,其實到達VIEW這裏是這樣的,經過store,這樣全部的狀態都集中到了這裏了。

經過 combineReducers,將多個reducer純函數結合爲一個。

 

接下來,container/app.js

 

import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import {fetchPostsIfNeeded,weekChoice,giftIdChoice,tabChange} from '../actions'
import * as TodoActions from '../actions'

class App extends Component {
  constructor(props) {
    super(props)
    this.weekChoiceFn = this.weekChoiceFn.bind(this)
    this.giftIdChoiceFn = this.giftIdChoiceFn.bind(this)
    this.tabFn = this.tabFn.bind(this)
  }

  //初始化渲染後觸發
  componentDidMount() {
    const { dispatch,isLastWeek,giftId} = this.props
    dispatch(fetchPostsIfNeeded(isLastWeek,giftId))
  }

  //每次接受新的props觸發
  componentWillReceiveProps(nextProps) {

    if ((nextProps.isLastWeek !== this.props.isLastWeek) || (nextProps.giftId !== this.props.giftId) ) {
      const { dispatch, isLastWeek ,giftId} = nextProps
      dispatch(fetchPostsIfNeeded(isLastWeek,giftId))
    }

  }

  // 上週仍是本週
  weekChoiceFn(isLastweek) {

    this.props.dispatch(weekChoice(isLastweek));
    
  }
  
  // 送出熱氣球星榜仍是魔術帽星榜
  giftIdChoiceFn(giftId) {
    this.props.dispatch(giftIdChoice(giftId));
    
  }

  tabFn (tabId) {

      this.props.dispatch(tabChange(3));
    
  }

  render() {
    const { receiveData } = this.props  //this.props裏面包含着全部的狀態
    return (
      <div>
            <a href="#" onClick={this.weekChoiceFn.bind(this,1)}>上週</a> 
            <br />
            <a href="#" onClick={this.weekChoiceFn.bind(this,0)}>本週</a>
            <br />
           <a href="#" onClick={this.giftIdChoiceFn.bind(this,401)}>熱氣球星榜</a><br />
           <a href="#" onClick={this.giftIdChoiceFn.bind(this,402)}>魔術帽星榜</a><br />
           <a href="#" onClick={this.tabFn}>
              tabId
            </a>
            <br />
            這裏是請求到的數據 <br />{JSON.stringify(receiveData)}<br />
      </div>
    )
  }
}


function mapStateToProps(state) {
  // 這裏很重要,這裏須要用到的狀態都要返回,否則沒法實現
  const { receiveData ,isLastWeek,giftId,tabIdState} = state
  return {
    receiveData,
    isLastWeek,
    giftId,
    tabIdState
  }
}

// function mapDispatchToProps(dispatch) {
//   return {
//     actions: bindActionCreators(TodoActions, dispatch)
//   }
// }
export default connect(
  mapStateToProps
  // mapDispatchToProps
)(App)

  其實注意點在這裏比較多,首先,全部的狀態是須要返回的。這個地方很重要。由於經過 mapStateToProps 這樣頂級組件才能夠拿到全部的狀態。

function mapStateToProps(state) {
  // 這裏很重要,這裏須要用到的狀態都要返回,否則沒法實現
  const { receiveData ,isLastWeek,giftId,tabIdState} = state
  return {
    receiveData,
    isLastWeek,
    giftId,
    tabIdState
  }
}

  其次,要拿到狀態值,能夠經過this.pros這裏拿,好比 const { receiveData } = this.props  ,咱們就經過this.props拿到了 receiveData的狀態值。

那麼如何知道狀態值需不須要更新你,能夠經過如下方法:

 //每次接受新的props觸發
  componentWillReceiveProps(nextProps) {

    if ((nextProps.isLastWeek !== this.props.isLastWeek) || (nextProps.giftId !== this.props.giftId) ) {
      const { dispatch, isLastWeek ,giftId} = nextProps
      dispatch(fetchPostsIfNeeded(isLastWeek,giftId))
    }

  }

   這裏,每次接受到新的props時就會觸發這個函數,裏面有一個參數,nextProps,就是最近的狀態值,咱們能夠和咱們舊的狀態值作對比,從而作相應的處理。好比,咱們只要isLastWeek或者giftId的參數不等,咱們就從新發一次請求。

  這個componentWillReceiveProps函數使比較重要的。否則你接收到新的參數也從新發送請求。因此要注意這一點。

  其餘部分的東西跟我以前的redux異步操做筆記的內容有點像,這裏就再也不進行介紹了。界面的渲染也不作了,偷一下懶,只作了幾個按鈕,有須要的能夠參考一下就行。本文跟以前寫的文章有點小相似,就不發佈到首頁了。

 

後記:

  關於react生態的學習就暫時告一段了,由於如今所處的部門是公司的活動組,基本都是一直在搞活動,從未中止過。因此都不是很大型的項目,不過這些活動用來試水還挺好的,很差的地方就是缺少一個大項目使用react生態的那種經驗。

  接下來的學習重心可能會從react的生態鏈中轉移開,關注一下其餘的東西。八月份到九月份的學習計劃是再提高一下本身的javascript水平,看兩本書《精通javascript》和《css權威指南》,迴歸一下基礎。而後十月份以後從新學習一下node.js, node.js是一個很迷茫的東西,由於若是沒有項目導向,學完就很容易忘記,因此以前學過的基本都忘光了。

前端水很深啊,共勉之!!!

相關文章
相關標籤/搜索