zoukankan      html  css  js  c++  java
  • 第3章 从Flux到Redux

    第3章 从Flux到Redux

    3.1 Flux

    单向数据流,React是用来替换Jquery的,Flux是以替换Backbone.js、Ember.js等MVC框架为主的。

    actionTypes.js定义action类型;

    actions.js定义action构造函数,决定了这个功能模块可以接受的动作;

    reducer.js定义这个功能模块如何响应action.js中定义的动作;

    views目录,包含这个功能模块中所有的React组件,包括傻瓜组件和容器组件;

    index.js这个文件把所有的角色导入,然后统一导出。

    一个Flux应用包含四个部分:

    Dispatcher,处理动作分发,维持Stror之间的关系;

    Store,负责存储数据和处理数据相关逻辑;

    Action,驱动dispatcher和JavaScript对象;

    View,视图部分,负责显示用户界面。

    如果非要把Flux 和MVC 做一个结构对比,那么, Flux 的Dispatcher 相当于MVC 的Controller, Flux 的Store 相当于MVC 的Model, Flux 的View 当然就对应MVC 的View了,至于多出来的这个Action ,可以理解为对应给MVC 框架的用户请求。

    MVC最大的缺点就是无法禁绝View和Model之间的直接对话。

    Flux应用实例:

    1、Dispatcher

      首先创造一个Dispatcher,Dispatcher的作用就是派发action,几乎所有应用都只需拥有一个Dispathcer,在src下创造一个唯一的Dispatcher对象

    import {Dispatcher} from 'flux'
    
    export default new Dispacther()
    

    2、Action

      代表一个动作,一个纯粹的数据对象。

      action对象必须有一个名为type的字段,代表这个action对象的类型,为了记录日志和debug方便,这个type应该是字符串类型。

      定义action通常需要两个文件,一个定义action类型,一个定义actio的构造函数(也称为action creator)。原因是store会根据action不同类型做不同操作。

      在src/Actiontypes.js中,定义action的类型

    //两个action类型 INCREMENT和DECREMENT,一个是点击+按扭,一个是点击-按扭
    export const INCREMENT = 'increment' export const DECREMENT = 'decrement'

      在src/Action.js中,定义action的构造函数,这里边定义的并不是action对象本身,而是能够产生并派发action对象的函数。

    //引入ActionTypes和AppDispatcher,直接使用Dispatcher
    import * as ActionTypes from './ActionTypes' import AppDispatcher from './AppDispatcher'
    //Action.js导出了两个action的构造函数increment和decrement,当这两个构造函数被调用的时候,创造了对象的action对象,并立即通过AppDispatcher.dispatch函数派发出去。 export const increment = (counterCaption) => { AppDispatcher.dispatch({ type: ActionTypes.INCREMENT, counterCaption: counterCaption }) } export const decrement = (counterCaption) => { AppDispatcher.dispatch({ type: ActionTypes.DECREMENT, counterCaption: counterCaption }) }

    3、Store

      一个Store也是一个对象,这个对象存储应用状态,同时还要接受Dispatcher派发的动作,根据动作来决定是否要更新应用状态。

       当Store的状态发生变化时,需要通知应用的其它部分做出响应,做出响应的当然是view部分,但是我们硬编码这种联系,应该用消息的方式建立Store和View的联系。这就是为什么让CounterStore 扩展成了EventEmitter.proptype,等于让CounterStore成了一个EventEmitter对象。

    一个EventEmitter的实例对象支持下列相关函数:

    emit函数,可以广播一个特定事件,第一个参数是字符串类型的事件名称;

    on函数,可以增加一个挂在这个EventEmitter对象特定事件上的处理函数,第一个参数是字符串类型的事件名称,第二个参数是处理函数;

    removeListener函数,和on函数做的事情相反,删除挂在这个EventEmitter对象特定事件上的处理函数,参数一样。

    import AppDispatcher from '../AppDispatcher'
    import * as ActionTypes from '../ActionTypes'
    import {EventEmitter} from 'events'
    import { Action } from 'rxjs/internal/scheduler/Action';
    
    const CHANGE_EVENT = 'change'
    
    const counterValues = {
        'First': 0,
        'Second': 10,
        'Third': 20
    }
    
    const CounterStore = Object.assign({},EventEmitter.prototype,{
        //让应用的其它模块可以读取当前的计数值
        getCounterValues:function(){
            return counterValues
        },
        //对CounterStore状态更新的广播
        emitChange:function(){
            this.emit(CHANGE_EVENT)
        },
        //添加监听函数
        addChangeListener:function(callback){
            this.on(CHANGE_EVENT, callback)
        },
        //删除监听函数
        removeChangeListener:function(callback){
            this.removeListener(CHANGE_EVENT, callback)
        }
    })
    

    把CounterStore注册到全局唯一的Dispatcher上去。Dispatcher有一个函数叫register,接受一个回调函数作为参数。返回值是一个token,这个token用于Sotre之前的同步,在CounterStore中暂时用不到。

    //把register返回值保存在CounterStore对应的dispatchToken字段上
    CounterStore.dispatchToken = AppDispatcher.register((action)=>{
        if(action.type === ActionTypes.INCREMENT){
            counterValues[action.counterCaption] ++;
            CounterStore.emitChange()
        }else if(action.type === ActionTypes.DECREMENT){
            counterValues[action.counterCaption] --;
            CounterStore.emitChange()
        }
    })
    
    export default CounterStore
    

     Flux的核心:当通register函数把一个回调函数注册到Dispatcher之后,所有派发给Dispatcher的action对象,都会传递到这个回调函数中来。

    import React, {Component} from 'react'
    import Counter from './Counter'
    
    class ControlPanel extends Component{
        
        render() {
            return (
                <div>
                    <Counter caption='First' />
                    <Counter caption='Second' />
                    <Counter caption='Third' />
                </div>
            )
        }
    }
    
    export default ControlPanel
    

      

    import React, {Component} from 'react'
    import * as Actions from '../Action'
    import CounterStore from '../stores/CounterStore'
    const buttonStyle = {
        margin: '10px'
    }
    class Counter extends Component{
        constructor(props){
            super(props)
            // console.log('enter constructor',props.caption)
    
            this.add = this.add.bind(this)
            this.math = this.math.bind(this)
            this.onChange = this.onChange.bind(this)
            this.state={
                count: CounterStore.getCounterValues()[props.caption]
            }
        }
    
        shouldComponentUpdate(nextProps, nextState){
            return (nextProps.caption !== this.state.caption ||
                    nextProps.count !== this.state.count)
        }
    
        componentDidMount(){
            CounterStore.addChangeListener(this.onChange)
        }
    
        componentWillUnmount(){
            CounterStore.removeAllListeners(this.onChange)
        }
    
        onChange(){
            const newCount = CounterStore.getCounterValues()[this.props.caption]
            this.setState({count: newCount})
        }
    
        add(){
            Actions.increment(this.props.caption)
        }
    
        math() {
            Actions.decrement(this.props.caption)
        }
    
        render(){
            console.log('enter render', this.props.caption)
            return(
                <div>
                    <button style={buttonStyle} onClick={this.add}>+</button>
                    <button style={buttonStyle} onClick={this.math}>-</button>
                    <span>count:{this.state.count}</span>
                    {/* <span>props:{this.props}</span> */}
                </div>
            )
        }
    }
    Counter.defaultProps={
        initValue: 0,
        onUpdate: f => f //默认是一个什么都不做的函数
    }
    
    export default Counter
    

    4、View

    存在于flux框架中的React组件需要实现以下几个功能:

    创建时要读取Store上状态来初始化组件内部状态;

    当Store上状态发生变化时,组件要立刻同步更新内部保持状态一致;

    View如果要改变Store状态,必须并且只能派发action。

    5、Flux的不足

      1.Store之间的依赖关系

      在Flux体系中,如果两个Store之间有逻辑关系,就必须用上Dispatcher的waitFor函数。

      2.难以进行服务器渲染

      3.Store混杂了逻辑和状态

      Store封装了数据和处理数据的逻辑,当我们需要替换一个Store的逻辑时,只能把整个Store整体替换掉,无法保持Store中的存储状态。

    3.2 Redux

    Redux的三个基本原则:

    唯一数据源(Single Source of Truth);

    保持状态只读(State is read-only);

    数据改变只能通过纯函数完成(Changes are made with pure functions)。

    1、唯一数据源

      唯一数据源指的是应用的状态数据应该只存储在唯一的一个Store上。

      Flux是状态数据分散在多个Store中,容易造成数据冗余。

      Redux就是整个应用只有一个Store,所有组件的数据源就是这个Store上的状态。

      这个唯一Store上的状态,是一个树的形象。

    2、保持状态只读

      不能去直接修改状态,要修改Store的状态,必须要通过派发一个action对象完成,这一点和flux没区别。

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

      纯函数就是Reducer,Redux就是Reducer+Flux。

    3.2.2 Redux实例

    ActionTypes.js

    export const INCREMENT = 'increment'
    export const DECREMENT = 'decrement'

    Action.js

    import * as ActionTypes from './ActionTypes'
    export const increment = (counterCaption) => {
        //Redux返回一个action 对象
       return {
            type: ActionTypes.INCREMENT,
            counterCaption: counterCaption
        }
    }
    
    export const decrement = (counterCaption) => {
        return {
            type: ActionTypes.DECREMENT,
            counterCaption: counterCaption
        }
    }
    

      

    Store.js

    这个文件输出全局唯一的Store

    import {createStore} from 'redux'
    import reducer from './Reducer'
    
    const initValues = {
        'First' : 0,
        'Secont' : 10,
        'Third' : 20
    }
    //第一个参数代表更新状态,第二个参数代表初始状态,第三个参数Store Enhancer,暂时用不上
    const store = createStore(reducer, initValues)
    
    export default store
    

      

    Reducer.js

    和Flux应用中注册的回调函数一样,与Flux不同的是多了一个state,Flux中没有,是因为state是由Store管理而不是由Flux。

    Redux把存储state的工作抽取出来交给Redux框架本身,让reducer只关心如何更新state,而不管state怎么存。

    import * as ActionTypes from './ActionTypes'
    
    export default (state, action) => {
        const {counterCaption} = action
        /**
         * return {...state,[counterCaption] : state[counterCaption] + 1}等同于
         * const newState = Object.assign({},state)
         * newState[counterCaption] ++
         * return newState
         */
        switch (action.type) {
            case ActionTypes.INCREMENT:
            //...操作符,表示把state所有字段扩展开
                return {...state,[counterCaption] : state[counterCaption] + 1}
            case ActionTypes.DECREMENT:
                return { ...state, [counterCaption]: state[counterCaption] - 1 }
            default:
                return state;
        }
    }
    

     ControlPanel.js

    import React, {Component} from 'react'
    import Counter from './Counter'
    import SumCounter from './SumCounter'
    
    class ControlPanel extends Component{
        
        render() {
            return (
                <div>
                    <Counter caption='First' />
                    <Counter caption='Second' />
                    <Counter caption='Third' />
                    <SumCounter />
                </div>
            )
        }
    }
    
    export default ControlPanel
    

     Counter.js

    import React, {Component} from 'react'
    import * as Actions from '../Action'
    import store from '../Store'
    const buttonStyle = {
        margin: '10px'
    }
    class Counter extends Component{
        constructor(props){
            super(props)
            // console.log('enter constructor',props.caption)
    
            this.add = this.add.bind(this)
            this.math = this.math.bind(this)
            this.getOwnState = this.getOwnState.bind(this)
            this.onChange = this.onChange.bind(this)
            this.state = this.getOwnState()
        }
    
        getOwnState(){
            // console.log(store.getState(),'state')
            return {
                value: store.getState()[this.props.caption]
            }
        }
    
        componentDidMount(){
            //通过Store的subscribe监听其变化,只要Store的状态发生变化就会调用这个组件的onChange方法。
            //增加监听的函数也可以写到构造函数中
            store.subscribe(this.onChange)
        }
        componentWillUnmount(){
            //把监听注销
            store.unsubscribe(this.onChange)
        }
        onChange(){
            this.setState(this.getOwnState())
        }
    
        add(){
            //派发action
            //action构造函数只负责创建对象,要派发action就需要调用store.dispatch函数
            store.dispatch(Actions.increment(this.props.caption))
            // Actions.increment(this.props.caption)
        }
    
        math() {
            store.dispatch(Actions.decrement(this.props.caption))
            // Actions.decrement(this.props.caption)
        }
    
        render(){
            const value = this.state.value
            const {caption} = this.props
            return(
                <div>
                    <button style={buttonStyle} onClick={this.add}>+</button>
                    <button style={buttonStyle} onClick={this.math}>-</button>
                    <span>{caption} count:{value}</span>
                </div>
            )
        }
    }
    
    export default Counter
    

      SumCounter.js

    import React, {Component} from 'react'
    import store from '../Store'
    
    class SumCounter extends Component{
        constructor(props){
            super(props)
    
            this.onChange = this.onChange.bind(this)
            this.getOwnState = this.getOwnState.bind(this)
            store.subscribe(this.onChange)
            this.state = this.getOwnState()
    
        }
    
        onChange(){
            this.setState(this.getOwnState())
        }
    
        componentWillUnmount(){
            store.unsubscribe(this.onChange)
        }
        getOwnState(){
            const state = store.getState()
            let sum = 0
            for(const key in state){
                if(state.hasOwnProperty(key)){
                    sum += state[key]
                }
            }
            return {sum}
        }
        render(){
            return(
                <div>Total: {this.state.sum}</div>
            )
        }
    }
    
    export default SumCounter
    

    3.2.3 容器组件和傻瓜组件

    在Redux框架下,一个React组件需要完成两个功能:

      1.和Redux Store打交道,读取Store状态,用于初始化组件的状态,同时还要监听Store的状态改变;当Store发生改变时,需要更新组件状态,驱动组件重新渲染;当需要更新

      Store状态时,就要派发action对象;

      2.根据当前props和state,渲染出用户界面。

      为了让一个组件只专注做一件事,可以把这个组件拆分成多个组件,让每个组件只专注做一件事。

      上面也说了在Redux框架下,一个React组件南非要完成两个功能,可以考虑拆分成两个组件,把两个组件嵌套起来,完成原本一个组件完成的所有任务。

      两个组件是父子关系。承担第一个任务的组件,也就是负责和Redux Store打交道的组件,处于外层,所以被称为容器组件(Container Component),又叫聪明组件;对于承担第二个任务的组件,也就是只专心负责渲染页面的组件,处于内层,叫做展示组件,又叫傻瓜组件。

      傻瓜组件就是一个纯函数,根据props产生的结果。

    Counter.js拆分

    import React, {Component} from 'react'
    import * as Actions from '../Action'
    import store from '../Store'
    const buttonStyle = {
        margin: '10px'
    }
    /*
     * 傻瓜组件
     * Counter组件完全没有state,只有一个render方法,所有的数据都来自props,这种组件叫“无状态”组件
     */
    // class Counter extends Component{
    //     render(){
    //         const {caption, add, math, value} = this.props
    //         return(
    //             <div>
    //                 <button style={buttonStyle} onClick={add}>+</button>
    //                 <button style={buttonStyle} onClick={math}>-</button>
    //                 <span>{caption} count:{value}</span>
    //             </div>
    //         )
    //     }
    // }
    
    //缩减版傻瓜组件,无状态组件,因为没有状态,不需要对象表示,所以连类都不需要了,对于只有一个render方法的组件,缩略为一个函数足矣。
    function Counter({ caption, add, math, value }){
        //解构赋值或者props参数
        // function Counter(props) {
        // const { caption, add, math, value } = props
        return (
            <div>
                <button style={buttonStyle} onClick={add}>+</button>
                <button style={buttonStyle} onClick={math}>-</button>
                <span>{caption} count:{value}</span>
            </div>
        )
    }
    
    //容器组件
    /**
     * CounterContainer承担了所有的和Store关联的工作,它的render函数所做的就是渲染傻瓜组件Counter而已,只负责传递必要的prop
     */
    class CounterContainer extends Component{
        constructor(props){
            super(props)
            // console.log('enter constructor',props.caption)
    
            this.add = this.add.bind(this)
            this.math = this.math.bind(this)
            this.getOwnState = this.getOwnState.bind(this)
            this.onChange = this.onChange.bind(this)
            this.state = this.getOwnState()
        }
    
        getOwnState(){
            // console.log(store.getState(),'state')
            return {
                value: store.getState()[this.props.caption]
            }
        }
    
        componentDidMount(){
            //通过Store的subscribe监听其变化,只要Store的状态发生变化就会调用这个组件的onChange方法。
            //增加监听的函数也可以写到构造函数中
            store.subscribe(this.onChange)
        }
        componentWillUnmount(){
            //把监听注销
            store.unsubscribe(this.onChange)
        }
        onChange(){
            this.setState(this.getOwnState())
        }
    
        add(){
            //派发action
            //action构造函数只负责创建对象,要派发action就需要调用store.dispatch函数
            store.dispatch(Actions.increment(this.props.caption))
            // Actions.increment(this.props.caption)
        }
    
        math() {
            store.dispatch(Actions.decrement(this.props.caption))
            // Actions.decrement(this.props.caption)
        }
    
        render(){
            const value = this.state.value
            const {caption} = this.props
            return(
                <Counter caption={caption} add={this.add} math={this.math} value={value} />
            )
        }
    }
    
    //export导出的是容器组件,对于这个视图模块来说,根本不会感受到傻瓜组件的存在,从外部看到的就只是容器组件。
    export default CounterContainer
    

    3.2.4 组件Context

    不能每一个组件用到Store都去引入一次,需要定义一个全局的,只有一个地方需要导入Store,这个位置应该在调用最顶层的React的位置。

    Context就是“上下文环境”,让一个树状组件上的所有组件都能访问一个共同的对象。

    首先上级要宣称自己支持context,并且提供一个函数来返回代表Context的对象。

    然后,这个上级组件下的所有子孙组件,只需要宣称自己需要这个context,就可以通过this.context访问到这个共同的环境对象。

    定义一个Provider.js组件

    import {Component} from 'react'
    import PropTypes from 'prop-types'
    
    /**
     * Provider也是一个React组件,它的render函数就是简单的把子组件渲染出来,在渲染上,Provider不做作何附加的事情。
     */
    class Provider extends Component{
        //这个函数返回的就是代表Context的对象
        //要求Provider的使用者通过prop传递进来store
        getChildContext(){
            return {
                store: this.props.store
            }
        }
        render(){
            /**
             * 每个React组件的props中都可以有一个特殊属性children,代表的是子组件
             * this.props.children就是两个Provider标签之间的<ControlPanel />
             * <Provider>
             *  <ControlPanel />
             * </Provider>
             */
            //把渲染工作交给子组件
            return this.props.children
        }
    }
    //为了让Provider能够被React认可为一个Context的提供者,还需要指定Provider的childContextTypes属性
    //类的childContextTypes,必须和getChildContext对应,只有两者都齐备,Provider的子组件才有可能访问到context.
    Provider.childContextTypes = {
        store: PropTypes.object
    }
    
    export default Provider

    入口文件index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import Provider from './redux_width_context/Provider'
    import store from './redux_width_context/Store'
    
    import ControlPanel from './redux_width_context/views/ControlPanel'
    
    ReactDOM.render(
        <Provider store={store}>
            <ControlPanel />
        </Provider>
    , document.getElementById('root'))
    

    ControlPanel.js

    import React, {Component} from 'react'
    import Counter from './Counter'
    import SumCounter from './SumCounter'
    
    class ControlPanel extends Component{
        render(){
            return(
                <div>
                    <Counter caption='First' />
                    <Counter caption='Second' />
                    <Counter caption='Third' />
                    <SumCounter />
                </div>
            )
        }
    }
    
    export default ControlPanel
    

     Counter.js

    import React, {Component} from 'react'
    import * as Actions from '../Action'
    // import store from '../Store'
    import PropTypes from 'prop-types'
    
    const buttonStyle={
        margin: '10px'
    }
    function Counter(props){
        const {cpation, add, math, value} = props
        return (
            <div>
                <button style={buttonStyle} onClick={add}>+</button>
                <button style={buttonStyle} onClick={math}>-</button>
                <span>{cpation} Count: {value}</span>
            </div>
        )
    }
    
    
    class CounterContainer extends Component{
        //因为定义了自己的构造函数,所以要用上第二个参数context
        constructor(props, context){
            console.log(context, context)
            //super的时候要带上context,这样才能上React组件初始化实例中的context,不然组件的其它部分就无法使用this.context
            //也可以用...arguments的方法
            super(props,context)
            this.getOwnState = this.getOwnState.bind(this)
            this.add = this.add.bind(this)
            this.math = this.math.bind(this)
            this.onChange = this.onChange.bind(this)
            this.context.store.subscribe(this.onChange)
            this.state=this.getOwnState()
        }
    
        getOwnState(){
            return {
                // value: store.getState()[this.props.caption]
                //所有的store访问者是通过this.context.store完成,this.context就是Provider提供的context对象
                value: this.context.store.getState()[this.props.caption]
            }
        }
    
        componentWillUnmount(){
            this.context.store.unsubscribe(this.onChange)
        }
    
        onChange(){
            this.setState(this.getOwnState())
        }
    
        add(){
            this.context.store.dispatch(Actions.increment(this.props.caption))
        }
    
        math(){
            this.context.store.dispatch(Actions.decrement(this.props.caption))
        }
    
        render(){
            const value = this.state.value
            return(
                
                <Counter caption={this.props} add={this.add} math={this.math} value={value} />
                
            )
        }
    }
    /**
     * 给CounterContainer类的contextTypes赋值和Provider.childContextTypes一样的值,两者必须一致,不然无法访问到context
     */
    CounterContainer.contextTypes = {
        store: PropTypes.object
    }
    export default CounterContainer
    

    3.2.5 React-Redux

      React应用改进的两个方法,

      第一是把一个组件拆分为容器组件和傻瓜组件,第二是使用React的Context来提供一个所有组件都可以直接访问的Context。

      React-Redux就是把这两种方法的套路部分抽取出来复用,这样每个组件的开发只需关注不同的部分就可以了。

    React-Redux和Redux的不同就是react-redux不再使用自己实现的Provider,而是从react-redux库导入Provider,

    react-redux的两个最主要功能:

      connect:连接容器组件和傻瓜组件

      Provider:提供包含store的context。

    1、connect函数具体做的工作:

      把Store上的状态转化为内层傻瓜组件的prop;

      把内层傻瓜组件中的用户动作转化为派送给Store的动作。

      一个是内层傻瓜对象的输入,一个是内层傻瓜对象的输出。

    Counter.js

    import React, {Component} from 'react'
    import * as Actions from '../Action'
    // import store from '../Store'
    import PropTypes from 'prop-types'
    import { connect } from 'react-redux';
    
    const buttonStyle={
        margin: '10px'
    }
    function Counter(props){
        const {cpation, onIncrement, onDecrement, value} = props
        return (
            <div>
                <button style={buttonStyle} onClick={onIncrement}>+</button>
                <button style={buttonStyle} onClick={onDecrement}>-</button>
                <span>{cpation} Count: {value}</span>
            </div>
        )
    }
    
    /**
     * 把Store上的状态转化为内层组件的Props
     * @param {*} state 
     * @param {*} ownProps 
     */
    function mapStateToProps(state, ownProps){
        return {
            value: state[ownProps.caption]
        }
    }
    
    /**
     * 把内层傻瓜组件中用户动作转化为派送给Store的动作,也就是把内层傻瓜组件暴露出来的函数类型的prop关联上dispatch函数的调用,
     * 每个prop代表的回调函数的主要区别就是dispatch函数的参数不同。
     * @param {*} dispatch 
     * @param {*} ownProps 
     */
    function mapDispatchToProps(dispatch, ownProps){
        return {
            onIncrement: () => {
                dispatch(Actions.increment(ownProps.caption))
            },
            onDecrement: () => {
                dispatch(Actions.decrement(ownProps.caption))
            }
        }
    }
    
    
    /**
     * 给CounterContainer类的contextTypes赋值和Provider.childContextTypes一样的值,两者必须一致,不然无法访问到context
     */
    CounterContainer.contextTypes = {
        store: PropTypes.object
    }
    /**
     * connect是react-redux提供的一个方法
     * 接收两个参数,执行结果依然是一个函数,
     * 这里有两个函数执行:
     * 第一次是connect函数的执行;
     * 第二次把Connect函数执行的结果再执行,这一次的参数是Counter傻瓜组件,最后产生的就是容器组件。
     */
    export default connect(mapStateToProps, mapDispatchToProps)(Counter)

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    // import Provider from './redux_width_context/Provider'
    import {Provider} from 'react-redux'
    import store from './redux_width_context/Store'
    import ControlPanel from './react_redux/views/ControlPanel'
    
    ReactDOM.render(
        <Provider store={store}>
            <ControlPanel />
        </Provider>
    , document.getElementById('root'))
    
    

      

    2、Provider

    react-redux要求store不光是一个object,而且必须是包含三个函数的object,这三个函数分别是:

    subscribe、dispatch、getState

      react-redux定义了Provider的componentWillReceiveProps函数,在React组件的生命周期中,componentWillReceiveProps函数在每次重新渲染时都会调用到,react-redux在componentWillReceiveProps函数中会检查这一次渲染时代表store的prop和上一次的是否一样。如果不一样,就会给出警告,必免多次渲染了不同的Redux Store。

      每个Redux应用都只能有一个Redux Store,在整个Redux的生命周期中都应保持Store的唯一性。

  • 相关阅读:
    Cs Round#54 E Late Edges
    Cs Round#54 D Spanning Trees
    python装饰器的理解
    java序列化,二进制和数据流
    netty的理解
    CenterOS卸载和安装MYSQL
    oracle的一些问题
    tomcat优化方案(转)
    Selector
    Buffer
  • 原文地址:https://www.cnblogs.com/huyanluanyu/p/10163827.html
Copyright © 2011-2022 走看看