| createStroe - 创建一个store
| reducer - 用来改变state的值,结合了action的动作来分别处理
| action - 要执行的事件
| combineReducers - 将多个reducer合到一起,赋给createStroe
| replaceReducer - 替换reducer,改变state修改逻辑
| subscribe - 侦听Store的变化
| store.getState - 获取Store中的state值
| store.dispatch - 触发action,改变state的值
| provider - 将Store传递给内部组件
| connect - 将组件与redux的状态、事件关联起来
| applyMiddleware - 加载中间件
| bindActionCreators - 直接调用dispatch(action)()
| react-redux - react与redux结合

Redux

react是纯View层的框架,需要数据流进行支撑, 单一的状态树,主流数据流框架: Flux、Redux

UI -> action -> reducer -> store -> state -> UI

Redux是非异步的流程,如果需要使用异步操作使用 Redux-saga

一、Flux四部分

    1、Dispatcher - 处理动作分发,维持Store之间的依赖关系

    2、Store - 负责存储数据和处理数据相关逻辑

    3、Action - 驱动Dispatcher的JS对象

    4、View - 视图部分,负责显示用户界面

    Dispatcher相当于Controller,Stroe相当于Modle,View相当于View,Action相当于用户的行为


二、Redux三个基本原则

    1、唯一数据源 - 状态数据只存储在唯一的一个Store上

    2、保持状态只读 - 不能直接修改Store的状态,需要派发一个action对象完成

    3、数据改变只能通过纯函数完成

安装

Facebook官方提出了Flux思想管理数据流,同时也给出了自己的实现方案Flux来管理React应用

实现Flux思想的类库 Redux、Flux、reflux

$ npm i redux --save                    // 安装redux

$ npm i react-redux --save            // redux与react结合

$ npm i react-router-redux --save     // redux-router

$ npm i redux-devtools-extension --save        // 安装可视化工具

    引用:import { composeWithDevTools } from 'redux-devtools-extension'

    在chrome中下载redux-devtools插件

    https://github.com/zalmoxisus/redux-devtools-extension#usage

$ npm i redux-thunk --save

$ npm i redux-logger --save

引用redux: import { createStore } from 'redux'

一、Redux流程:

    1、view直接触发dispatch

    2、dispatch将action发送到reducer中后,根节点上会更新props,改变全局view

    3、redux将view和store的绑定从手动编码中提取出来,放在了统一规范放在了自己的体系中    

Action

action指令并不能直接改变state,action必须是json

let todoId = 0;
export const addTodo = (text) => {
    return {
        type: 'ADD_TODO',    // 处理的类型(必要)
        id: todoId++,            // 存储数据当前的id标识
        text                            // 传入的内容
    }
}

通过 store.dispatch(action) 来派发到reducer中, 在reducer中改变state的值

Reducer

接收一个action值,纯方法不能定义ajax定义时间等,需要传入一个旧的状态在返回一个新的状态给action, 根据定义的action名对应处理state,并改变state值

let todo = (state, action) => {

    // 这里通过action的type的值对应不同的事来处理
    switch(action.type){
        case 'ADD_TODO':
            return {
                state + 1;
            }
            break;

        /* 上面只是单个add_todo, 这里要写成多个add_todo
        case 'ADD_TODO':
            return [
                {
                    id: action.id,
                    text: action.text,
                    completed: false
                },
                ...state
            ]
            break;            
        */

        case 'SUB_TODO':
            return {
                state - 1;
            }
            break;

        default: state;
    }
}

1、combineReducers - 将多个的reducer合成一个

    // 目录 reducer/index.js
    import {combineReducers} from "redux";
    import reducerA from "./reducerA";
    import reducerB from "./reducerB";

    export default combineReducers({
        reducerA,
        reducerB,
    });

    // createStore调用reducer
    import reducers from './reducer/'
    let store = createStore(reducers)

Store

Store是来保存数据的容器,只有一个Store,可以根据不同业务拆分成多个reducer, createStroe(reducer, 初始化的)

import { createStore } from 'redux';
import reducer from './reducers'        // 获取定义的reducer

let stroe = createStroe(reducer);    // 使用createStore(reducer, initialState初始化state, 中间件传入)来创建一个数据仓库
store.dispatch();

方法:

    1、store.getState(): 获取State值

    2、store.dispatch(action): 触发action,修改state的值

    3、store.subscribe(listener),用来订阅状态的变化

        添加一个变化的监听器,每当dispatch action的时候就会执行

        // App组件
        class App extends Component {
            constructor(props){
                super(props);
                this.state = {
                    num: 0
                }
            }

            componentDidMount() {
                store.subscribe(this.handleChange.bind(this));
            }

            handleChange(){
                this.setState({num: store.getState()})
            }

            addClick(){
                store.dispatch({type: 'add'});
            }

            subClick(){
                store.dispatch({type: 'sub'});
            }

            showState(){
                debugger;
                console.log(store.getState())
            }

            render() {
                return (
                    <div className = "box" >
                        {this.state.num}
                        <input type="button" onClick={this.addClick} value="+" />
                        <input type="button" onClick={this.subClick} value="-" />
                        <input type="button" onClick={this.showState} value="查看state" />
                    </div>
                )
            }
        }

State

state用于存储数据,state是只读的,改变需要触发action

state树就是一个对象,所有的reducer的都会加到state中,取state值,store.getState().

react-redux

$ npm i react-reudx --save        // 安装

一、provider(store, children) - 将Store传到子组件中,一般用在入口文件

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {Provider} from "react-redux";
    import Store from "src/store";
    import App from 'src/components/app';
    import Chat from 'src/pages/Chat/Index';

    // 1、使用Store让所有组件访问到
    ReactDOM.render(
        <Provider store={Store}>
            <App>
                <Chat />
            </App>
        </Provider>,
        document.getElementById('app')
    );

    // 2、如果使用路由
    ReactDOM.render(
        <Provider store={store}>
            <Router ref="router" history={hashHistory}>
                <Route path='/' component={Index}>
                    <IndexRoute  component={MainPage}></IndexRoute>
                </Route>
            </Router>
        </Provider>
    )


二、connect(mapStateToProps, mapDispatchToProps)(Component连接组件) - 连接react组件与redux store

    1、mapStateToProps(state, ownProps) - 侦听Store的变化

            state参数 - 如果Store中有变化此方法就会被调用,该回调函数返回一个纯对象,这个对象会与组件的props合并

            ownProps参数 - 该参数的值为会飞经到组件的props,

    2、mapDispatchToProps - 逻辑输出,相当于用store.dispatch(actionType) 来发出操作指令,来改变state值

    Example:

        // app.jsx
        <Provider store={store}>
            <Router ref="router" history={hashHistory}>
                <Route path='/' component={Index}>
                    <IndexRoute  component={MainPage}></IndexRoute>
                </Route>
            </Router>
        </Provider>

        // Index.jsx
        import React, { Component } from 'react';
        import ReactDOM from 'react-dom';
        import { bindActionCreators } from "redux";
        import { connect } from "react-redux";
        import actions from "src/actions";

        class wechat extends Component {
            constructor(props){
                super(props);
                this.state = {

                }
            }

            componentDidMount(){
                let { ACTIONS } = this.props;
                ACTIONS.chat_init();
            }

            render(
                // ...
            )
        }

        let mapStateToProps=(state)=>{
            let {sessions,user} = state.chatIndex;
            return {
                _sessions:sessions,
                _user:user
            };
        }; 

        let mapDispatchToProps=(dispatch)=>{
            return {
                ACTIONS: bindActionCreators(actions, dispatch)
            };
        };

        export default connect(mapStateToProps,mapDispatchToProps)(wechat);

container组件

引用Redux将组件分两类container和component

container - 组件中使用redux获取数据,状态更新,从Redux获取state

bindActionCreators()

是通过dispatch将action包裹起来,这样可以通过bindActionCreators创建的方法,直接调用dispatch(action)(隐式调用)

import * as oldActionCreator from './action.js'

let newAction = bindActionCreators(oldActionCreator,dispatch)

调试工具

两种方法 1、下载redux-devtools包      2、chrome or Firfix 下载 Redux DevTools插件

1、使用Redux DevTools插件 代码中需要加配置

    https://github.com/zalmoxisus/redux-devtools-extension

    /* eslint-disable no-underscore-dangle */        // +
    let store = createStore(
        comput,
        window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()        // +
    )
    /* eslint-enable */                                // +


2、redux-devtool 包的使用 https://github.com/gaearon/redux-devtools

项目目录

|- src
|-- reduxs     // redux
|------ actions
|------ reducers
|-- router    // 路由
|-- util    // 工具

react-router-redux路由

redux管理的是应用状态(state),router管理的是路由,react-router-redux是保持路由与应用状态(state)同步

$ npm install --save react-router-redux        // 安装

原理:允许使用react router库中的api,使用redux一样去管理应用状态state

https://www.jianshu.com/p/bccca5bb6338
http://blog.csdn.net/isaisai/article/details/78086913
https://segmentfault.com/a/1190000007862103

http://blog.csdn.net/sinat_17775997/article/details/69218382

| http://cn.redux.js.org/index.html // Redux中文文档
| http://www.liuyiqi.cn/2016/01/19/r2-counter/
| https://github.com/lewis617/react-redux-tutorial
| https://github.com/xgrommx/awesome-redux
| https://www.jianshu.com/p/bccca5bb6338 // react-router-redux 保持路由与应用状态(state)同步
| http://www.redux.org.cn/ // react-redux
| https://www.jianshu.com/p/1a2f3db4af61 // react-redux
| https://www.jianshu.com/p/9873d4ccb891 // connect 原理
| https://github.com/GuoYongfeng/redux-complete-sample
| https://github.com/gaearon/redux-devtools
| http://www.aliued.com/?p=3204
| http://blog.csdn.net/liwusen/article/details/54138854 // bindActionCreators
| http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
| https://github.com/allan2coder/React-SPA/blob/master/src/store/configureStore.dev.js // 示例 router3.0