• 【原】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是一个很迷茫的东西,因为如果没有项目导向,学完就很容易忘记,所以之前学过的基本都忘光了。

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

  • 相关阅读:
    Java经典习题7
    Java经典习题6
    java经典习题5
    前后端分离开发——模拟数据mock.js
    微信网页第三方登录原理
    TP5常量
    TP5
    健忘的正则
    JS正则
    apache配置修改
  • 原文地址:https://www.cnblogs.com/xianyulaodi/p/5691382.html
Copyright © 2020-2023  润新知