【原】react+redux實戰


摘要:因為最近搞懂了redux的異步操作,所以覺得可以用react+redux來做一個小小的項目了,以此來加深一下印象。切記,是小小的項目,所以項目肯定是比較簡單的啦,哈哈。

 

項目效果圖如圖所示:(因為一張圖實現起來太長了 ,所以切成了兩半,略丑,罪過!!)

 

 

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

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

 

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

 

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

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

 

是的,不要搞那么復雜,這個活動頁就這幾個狀態的,不過這樣對於入門很有好處,因為比較好理解嘛

talk is cheap,show me the code。

來看我們的 actions

 

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';

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

// 1、上周和本周的切換  
// 2、點擊四個tab的切換
// 3、排行版和規則頁的切換

/*
  上周和本周的 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有一下幾點特性,

  1、它是一個純函數。

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

  3、它返回一個新的狀態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是一個很迷茫的東西,因為如果沒有項目導向,學完就很容易忘記,所以之前學過的基本都忘光了。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM