摘要:由于最近搞懂了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是一个很迷茫的东西,由于若是没有项目导向,学完就很容易忘记,因此以前学过的基本都忘光了。
前端水很深啊,共勉之!!!