ReactRedux

从 Redux 到 React-Redux

2017-06-01  本文已影响202人  柏丘君

上篇文章中,我们用 React 结合 Redux 实现了一个小计算器,并将组件拆分为容器组件和木偶组件,本文再基于这个例子继续做一些拓展,并最终引出 React-Redux。

计算器应用的问题

对于计算器小应用,我们已经实现了其所有的功能,因此不再有功能上的问题了,那么还有没有可以优化的地方呢?当然。
在这个例子中,每个容器组件都会引入 store,这样会产生两个问题:

或许你会想:如果有个全局变量就好了,将 store 存放在全局变量中,就不用每次都引入了,也不用关心 store 的引用路径了。
那么可不可以提供一个最外层的组件,让其提供 store 对象,然后子组件直接去取用这个对象呢?当然可以。这里就需要用到我们前面说过的 context,可以在这篇文章中查看相应的内容。

context 回顾

context 是组件树上的一个全局属性,这里先回顾一下 context 的使用:

定义 Provider 组件

下面,我们来定义一个 Provider 组件,用来在 context 中存放 store 对象,新建一个 Provider.js 文件:

import React,{ Component } from "react";
import PropTypes from "prop-types";

export default class Provider extends Component{
    // 声明 context 属性的类型
    static childContextTypes = {
        store:PropTypes.object.isRequired,
    }

    // 返回初始的 context
    getChildContext(){
        return{
            // store 对象由 Provider 组建的 props 传入
            store:this.props.store,
        }
    }

    render(){
        // 直接渲染子节点
        return this.props.children;
    }
}

修改 App.js,让 Provider 作为组件树的根节点,并向其传入 store:

import React,{ Component } from "react";
import Counter from "./ContainerCounter";
import Provider from "./Provider";
import store from "./Store/store";

export default class App extends Component{
    render(){
        return(
            <Provider store = { store }>
                <Counter />
            </Provider>
        );
    }
}

修改 ContainerCounter.js,从 context 中获取 store:

import React,{ Component } from "react";
import PropTypes from "prop-types";
import ACTIONS from "./Store/actions";
import UICounter from "./UICounter";

export default class ContainerCounter extends Component{
    // 声明自己需要取用 context 属性
    static contextTypes = {
        store:PropTypes.object.isRequired,
    }

    constructor(props,context) {
        super(props,context);
        // 获取初始状态
        this.state = {
            // 使用 this.context.store 代替原先的 store
            value:this.context.store.getState().value,
        };
    }

    componentWillMount() {
        // 监听 store 变化
        this.context.store.subscribe(this.watchStore.bind(this));
    }

    componentWillUnmount() {
        // 对 store 变化取消监听
        this.context.store.unsubscribe(this.watchStore.bind(this));
    }

    // 监听回调函数,当 store 变化后执行
    watchStore(){
        // 回调函数中重新设置状态
        this.setState(this.getCurrentState());
    }

    // 从 store 中获取状态
    getCurrentState(){
        return{
            value:this.context.store.getState().value,
        }
    }

    // 增加函数
    increase(){
        // 派发 INCREMENT Action
        this.context.store.dispatch(ACTIONS.increament());
    }

    // 减少函数
    decrease(){
        // 派发 DECREAMENT Action
        this.context.store.dispatch(ACTIONS.decreament());
    }

    render(){
        return(
            <UICounter
                increase = { this.increase.bind(this)}
                decrease = { this.decrease.bind(this)}
                value = { this.state.value }
            />
        );
    }
}

效果和原先保持一致:

利用 context 获取 store.gif

使用 React-Redux

通过前面的例子,我们对应用进行了如下改进:

前面说到,Redux 作为一款状态管理工具,可以和任意的框架结合使用,当然,还有一个更加适用于 React 开发的 React-Redux 库。其所作的工作和我们前面的差不多,但增加了不少的易用性。使用 React-Redux 前需要先进行安装:

npm install --save react-redux

下面说一下 React-Redux 库中的两个概念:

connect 函数接受两个参数,这两个参数都是函数:

下面看一下代码实现:
App.js:

import React,{ Component } from "react";
import Counter from "./ContainerCounter";
// 从 react-redux 中引入 Provider 组件
import { Provider } from "react-redux";
import store from "./Store/store";

export default class App extends Component{
    render(){
        return(
            <Provider store = { store }>
                <Counter />
            </Provider>
        );
    }
}

ContainerCounter.js:

import { connect } from "react-redux";
import ACTIONS from "./Store/actions";
import UICounter from "./UICounter";

function mapStateToProps(state){
    return{
        value:state.value
    }
}

function mapDispatchToProps(dispatch){
    return{
        // 增加
        increase(){
            // 派发 action
            dispatch(ACTIONS.increament());
        },
        // 减少
        decrease(){
            dispatch(ACTIONS.decreament());
        }
    }
}

const ContainerCounter = connect(
    mapStateToProps,
    mapDispatchToProps,
)(UICounter);

export default ContainerCounter;

最终效果和前面一样。
可见,使用 react-redux 后,逻辑变得更加清晰了,也减少了许多操作,比如对 store 进行监听(subscribe),声明子组件的 context 等。这些事情 react-redux 都帮我们做了,我们只需定义两个函数(mapStateToProps,mapDispatchToProps)传递给 connect 函数,connect 函数调用的结果也是一个函数,我们再将木偶组件传入这个结果函数,就自动生成了容器组件。更易维护,代码量更少。
除此之外,react-redux 还进行了一些优化的处理,比如 shouldComponentUpdate 的优化等,但核心原理没有变,只是进行了一层封装处理。
因此,在使用 react-redux 之前,最好对 Redux 有一些了解,否则会因为不清楚内部的原理而成为代码搬运工。

总结

本文首先从全局 store 入手,构造了 Redux 版本的 Provider 组件,在此基础上引入了更加方便的 react-redux 库,更加适用于 React 开发,提升了开发效率和项目的可维护性。

完。

上一篇下一篇

猜你喜欢

热点阅读