• redux 及 相关插件 项目实战


    目录结构

    +-- app
    |   +-- actions
    |       +-- index.js
    |   +-- components
    |       +-- content.js
    |       +-- footer.js
    |       +-- searchBar.js
    |   +-- constants
    |       +-- ActionTypes.js
    |   +-- containers
    |       +-- App.js
    |   +-- reducers
    |       +-- index.js
    |       +-- items.js
    |       +-- filter.js
    |   +-- utils
    |   +-- configureStore.js
    |   +-- index.js
    +-- css
    |   +-- pure.min.css
    +-- index.html

    Index.js

    在入口文件中,我们需要把App和redux建立起联系。Provider是react-redux提供的组件,它的作用是把store和视图绑定在了一起,这里的Store就是那个唯一的State树。当Store发生改变的时候,整个App就可以作出对应的变化。{() => }是声明了一个返回的函数传进Provider的props.children里,这个方法将会在React的 0.14版本得到简化。

    /* app/index.js */
     
    import React from 'react';
    import { Provider } from 'react-redux';
    import App from './containers/App';
    import configureStore from './configureStore';
     
    const store = configureStore();
     
    React.render(
        <div>
            <Provider store={store}>
                {() => <App /> }
            </Provider>
        </div>,
        document.getElementById('app'));

    Constants

    keyMirror这个方法非常的有用,它可以帮助我们轻松创建与键值key相等的常量。

    /* app/constants/actionTypes.js */
     
    import keyMirror from 'react/lib/keyMirror';
     
    export default keyMirror({
        ADD_ITEM: null,
        DELETE_ITEM: null,
        DELETE_ALL: null,
        FILTER_ITEM: null
    });
     
    // 等于
    // export const ADD_ITEM = 'ADD_ITEM';
    // export const DELETE_ITEM = 'DELETE_ITEM';
    // export const DELETE_ALL = 'DELETE_ALL';
    // export const FILTER_ITEM = 'FILTER_ITEM';

    Actions

    Action向store派发指令,action 函数会返回一个带有 type 属性的 Javascript Plain Object,store将会根据不同的action.type来执行相应的方法。addItem函数的异步操作我使用了一点小技巧,使用redux-thunk中间件去改变dispatch,dispatch是在View层中用bindActionCreators绑定的。使用这个改变的dispatch我们可以向store发送异步的指令。比如说,可以在action中放入向服务端的请求(ajax),也强烈推荐这样去做。

    /* app/actions/index.js */
     
    import { ADD_ITEM, DELETE_ITEM, DELETE_ALL, FILTER_ITEM } from '../constants/actionTypes';
     
    export function addItem(item) {
        return dispatch => {
           setTimeout(() => dispatch({type: ADD_ITEM}), 1000)
        }
    }
    export function deleteItem(item, e) {
        return {
           type: DELETE_ITEM,
           item
        }
    }
    export function deleteAll() {
        return {
           type: DELETE_ALL
        }
    }
    export function filterItem(e) {
        let filterItem = e.target.value;
        return {
           type: FILTER_ITEM,
           filterItem
        }
    }

    Reducers

    Redux有且只有一个State状态树,为了避免这个状态树变得越来越复杂,Redux通过 Reducers来负责管理整个应用的State树,而Reducers可以被分成一个个Reducer。

    Reduce在javascript Array的方法中出现过,只是不太常用。简单快速的用代码样例来回顾一下:

    /* Array.prototype.reduce */
     
    var arr = [1,2,3,4];
    var initialValue = 5;
    var result = arr.reduce(function(previousValue, currentValue) {
        return previousValue + currentValue
    }, initialValue)
    console.log(result)
    // 15
    // 该回调函数的返回值为累积结果,并且此返回值在下一次调用该回调函数时作为参数提供。
    // 整个函数执行的过程大致是这样 ((((5+1)+2)+3)+4)

    回到Redux中来看,整个的状态就相当于从[初始状态]merge一个[action.state]从而得到一个新的状态,随着action的不断传入,不断的得到新的状态的过程。(previousState, action) => newState,注意:任何情况下都不要改变previousState,因为这样View层在比较State的改变时只需要简单比较即可,而避免了深度循环比较。Reducer的数据结构我们可以用immutable-js,这样我们在View层只需要react-immutable-render-mixin插件就可以轻松的跳过更新那些state没有发生改变的组件子树。

    /* app/reducers/items.js */
     
    import Immutable from 'immutable';
    import { ADD_ITEM, DELETE_ITEM, DELETE_ALL } from '../constants/actionTypes';
     
    const initialItems = Immutable.List([1,2,3]);
     
    export default function items(state = initialItems, action) {
        switch(action.type) {
            case ADD_ITEM:
                return state.push( state.size !=0 ? state.get(-1)+1 : 1 );
            case DELETE_ITEM:
                return state.delete( state.indexOf(action.item) );
            case DELETE_ALL:
                return state.clear();
            default:
                return state;
        }
    }
    连接reducers

    Redux提供的combineReducers函数可以帮助我们把reducer组合在一起,这样我们就可以把Reducers拆分成一个个小的Reducer来管理Store了。

    /* app/reducers/index.js */
     
    import { combineReducers } from 'redux';
    import items from './items';
    import filter from './filter';
     
    const rootReducer = combineReducers({
      items,
      filter
    });
     
    export default rootReducer;

    Middleware

    在Redux中,Middleware 主要是负责改变Store中的dispatch方法,从而能处理不同类型的 action 输入,得到最终的 Javascript Plain Object 形式的 action 对象。

    redux-thunk为例子:

    /* redux-thunk */ 
    export default function thunkMiddleware({ dispatch, getState }) {
      return next =>
         action =>
           typeof action === ‘function’ ?
             action(dispatch, getState) :
             next(action);
    }

    当ThunkMiddleware 判断action传入的是一个函数,就会为该thunk函数补齐dispatch和getState参数,否则,就调用next(action),给后续的Middleware(Middleware 插件可以被绑定多个)得到使用dispatch的机会。

    /* app/configureStore.js */
     
    import { compose, createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from './reducers';
     
    var buildStore = compose(applyMiddleware(thunk), createStore)
    export default function configureStore(initialState) {
       return buildStore(rootReducer, initialState);
    }

    UI

    智能组件和木偶组件,因为本文主要是介绍Redux,对这个感兴趣的同学可以看一下这篇文章Smart and Dumb Components。本项目中在结构上会把智能组件放在containers中,木偶组件放于components中。

    containers

    智能组件,会通过react-redux函数提供的connect函数把state和actions转换为旗下木偶组件所需要的props。

    /* app/containers/App.js */
     
    import React from 'react';
    import SearchBar from '../components/searchBar';
    import Content from '../components/content';
    import Footer from '../components/footer';
    import { connect } from 'react-redux';
    import ImmutableRenderMixin from 'react-immutable-render-mixin';
    import * as ItemsActions from '../actions';
    import { bindActionCreators } from 'redux';
     
    let App = React.createClass({
         mixins: [ImmutableRenderMixin],
         propTypes: {
             items: React.PropTypes.object,
             filter: React.PropTypes.string
         },
         render() {
             let styles = {
                  '200px',
                 margin: '30px auto 0'
             }
             const actions = this.props.actions;
             return (
                 <div style={styles}>
                     <h2>Manage Items</h2>
                     <SearchBar filterItem={actions.filterItem}/>
                     <Content items={this.props.items} filter={this.props.filter} deleteItem={actions.deleteItem}/>
                     <Footer addItem={actions.addItem} deleteAll={actions.deleteAll}/>
                 </div>
             )
         }
     })
     
    export default connect(state => ({
         items: state.items,
         filter: state.filter
    }), dispatch => ({
         actions: bindActionCreators(ItemsActions, dispatch)
    }))(App);
    components

    木偶组件,各司其职,没有什么关于actions和stores的依赖,拿出项目中也可独立使用,甚至可以和别的actions,stores进行绑定。

    • SearchBar:查找Item。
    • Content:控制Items的显示,删除一个Item。
    • Footer:新增Item,删除全部Item。

    调试工具

    使用redux-devtools调试,为你在开发过程中带来乐趣。

    /* app/index.js */
     
    function renderDevTools(store) {
      if (__DEBUG__) {
        let {DevTools, DebugPanel, LogMonitor} = require('redux-devtools/lib/react');
        return (
          <DebugPanel top right bottom>
            <DevTools store={store} monitor={LogMonitor} />
          </DebugPanel>
        );
      }else {
        return null;
      }
    }
     
    React.render(
        <div>
            <Provider store={store}>
                {() => <App /> }
            </Provider>
            {renderDevTools(store)}
        </div>,
      document.getElementById('app'));
    /* app/configureStore.js */
     
    var buildStore;
    if(__DEBUG__) {
      buildStore = compose(
        applyMiddleware(thunk),
        require('redux-devtools').devTools(),
        require('redux-devtools').persistState(window.location.href.match(/[?&]debug_session=([^&]+)/)),
        createStore
      )
    }else {
      buildStore = compose(applyMiddleware(thunk), createStore)
    }
     
    export default function configureStore(initialState) {
      return buildStore(rootReducer, initialState);
    }

    在你的代码中加上上面的两段代码,运行npm run debug命令,就可以用调试工具来管理你的项目了。

    效果图:

    https://github.com/matthew-sun/redux-example

  • 相关阅读:
    函数式宏定义与普通函数
    linux之sort用法
    HDU 4390 Number Sequence 容斥原理
    HDU 4407 Sum 容斥原理
    HDU 4059 The Boss on Mars 容斥原理
    UVA12653 Buses
    UVA 12651 Triangles
    UVA 10892
    HDU 4292 Food
    HDU 4288 Coder
  • 原文地址:https://www.cnblogs.com/crazycode2/p/9139625.html
Copyright © 2020-2023  润新知