摘要:由於最近搞懂了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
// 因爲目前大多數瀏覽器原生還不支持它,建議你使用 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的代碼:
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純函數結合爲一個。
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是一個很迷茫的東西,由於若是沒有項目導向,學完就很容易忘記,因此以前學過的基本都忘光了。
前端水很深啊,共勉之!!!