当前位置 博文首页 > 小创:React 入门-redux 和 react-redux

    小创:React 入门-redux 和 react-redux

    作者:小创 时间:2021-01-24 10:44

    React 将页面元素拆分成组件,通过组装展示数据。组件又有无状态有状态之分,所谓状态,可以简单的认为是组件要展示的数据。React 有个特性或者说是限制单向数据流,组件的状态数据只能在组件内部修改,对于其他组件是只读的,想要修改只能通过组件提供的接口回调。

    随着组件数量的增多,组件间状态数据共享的复杂性也会随之增加,如果仅使用 React 组件内的 State 可能会导致程序的流程混乱,代码难以维护。

    原文链接:https://www.chuonye.com/archives/react-redux.html

    1. 引入 Redux

    为什么这么说呢,我们来看一个图:

    redux

    原始的 React,当最底层组件需要改变数据时,如果数据在父组件,回调方法层层传递即可;如果在兄弟组件,那就需要借助一个中间组件,当然了也有办法直接与兄弟组件通信。可以想象,随着应用复杂程度的提高,组件间通信使用的各种直接或间接回调,可能会导致代码乱成一团。

    此时,Redux 就派上用场了,如上图所示,它把应用的所有状态-数据存储在一个地方,并称之为Store,组件间不直接通信,而是把变化的数据推给 Store,需要根据状态变化重新渲染的组件通过订阅 Store 来实现。

    Redux 在设计与实现时,遵循三大原则或者说规范限制

    1.1 唯一数据源

    整个应用程序的状态数据仅存储在一个 Store 中,数据就存储在一个大的对象树(object tree)中。

    1.2 只读的 Store

    唯一的 Store 也是只读的,应用程序无法直接修改状态数据。Store 对象本身的 API 非常少,仅有四个方法:

    • getState() : 获取当前状态数据
    • dispatch(action) : 推送触发变化
    • subscribe(listener) : 订阅数据变化
    • replaceReducer(nextReducer)

    显而易见,没有提供设置状态的方法。其中的 dispatch(action) 是唯一改变数据的方法,比如:

    var action = {
      type: 'ADD_USER',
      user: {name: 'chuonye.com'}
    };
    
    store.dispatch(action);
    

    dispatch 方法会将 action 传递给 Redux,action 就是一个普通对象,包含触发的操作类型以及数据。

    1.3 使用纯函数更改数据

    Redux 接收到 action 后,会使用一个纯函数来处理,这些函数被称为 Reducers

    var someReducer = function(state, action) {
      ...
      return state;
    }
    

    Reducer 接收当前的 state 和 action 作为参数,它也不能直接修改原有的数据,而是通过返回一个新的 state 来修改。

    总的来说,Redux 是一个帮助应用统一管理状态数据的工具,它遵循严格的单向数据流(Store 只读)设计,使得应用的行为变得可预测且容易理解。

    react-redux-stream

    2. React-Redux

    Redux 一般和 React 这类框架搭配使用,为了方便与 React 集成,Redux 官方提供了一个 react-redux 绑定库。react-redux 将组件划分为容器组件UI组件其他组件,其中:

    • 容器组件:与 Redux-Store 交互,分派 Action,监听 state 变化,负责数据管理和业务逻辑
    • UI 组件:无状态,负责数据的展示,样式,排版,数据来源于 props 属性
    • 其他组件:无法明确区分是容器还是 UI 的组件,或者本身比较简单没有拆分必要的组件

    从整体来看,在使用上,应用代码分层设计,结构如下:

    react-redux

    我们不妨结合着数据库,来理解下每个层次的意思,从下往上:

    • Store 可以理解成数据库服务,其中的 State 是一个数据库,State 对象树的各个部分,可以理解为对应不同的
    • Reducer 就是实际对 State 增删改的,它按逻辑拆分成多个 子reducer,分别对对象树的不同部分或者说对不同的表进行操作,就如同一个 SQL 执行引擎
    • Action 就是一系列触发改变的动作以及数据,就如同一条条 SQL 语句
    • 容器组件和 UI 组件就如同 Java 中设计的 DAO 和 Controller

    这些设计怎么有点熟悉,跟 Java 是越来越像了。下面简单写个例子,看看到底怎么用。

    3. 实例:Counter 计数器

    接下来我们按照使用原生 React,使用 Redux 和使用 React-Redux的顺序分别实现一个 Counter 计数器的功能。

    3.1 原生 React 实现

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    
    class Counter extends Component {
        constructor(props) {
            super(props);
            this.state = { value: 0 }; // 状态数据
        }
        render() {
            return (
                <div>
                    <p>state: {this.state.value}</p>
                    <button onClick={() => this.handleIncrement()}>+1</button>
                    <button onClick={() => this.handleDecrement()}>-1</button>
                </div>
            );
        }
        // 处理方法
        handleIncrement() {
            let curVal = this.state.value + 1;
            this.setState({value: curVal});
        }
        handleDecrement() {
            let curVal = this.state.value - 1;
            this.setState({value: curVal});
        }
    }
    // 渲染
    ReactDOM.render(<Counter />, document.getElementById('root'))
    

    3.2 使用 Redux 实现

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { createStore } from 'redux';
    
    // 接收 action 并处理,签名:(state, action) => newState
    function reducer(state = {value: 0}, action) {
        let curVal = state.value;
        switch (action.type) {
            case 'INCREMENT':
                return {value: curVal+1};
            case 'DECREMENT':
                return {value: curVal-1};
            default:
                return state;
        }
    }
    // 创建 Redux store
    const store = createStore(reducer);
    
    const render = () => ReactDOM.render(
        <div>
            <p>state: {store.getState().value}</p>
            {/* dispatch 通知 store 改变数据 */}
            <button onClick={()=>{store.dispatch({type: 'INCREMENT'})}}>+1</button>
            <button onClick={()=>{store.dispatch({type: 'DECREMENT'})}}>-1</button>
        </div>,
        document.getElementById('root')
    )
    
    render();
    // 订阅 store,状态改变时更新 UI
    store.subscribe(render);
    

    3.3 使用 React-Redux 实现

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    import { createStore } from 'redux';
    import { Provider, connect } from 'react-redux';
    
    // 接收 action 并处理
    function reducer(state = {value: 0}, action) {
        let curVal = state.value;
        switch (action.type) {
            case 'INCREMENT':
                return {value: curVal+1};
            case 'DECREMENT':
                return {value: curVal-1};
            default:
                return state;
        }
    }
    // 定义一个 UI 组件,用于展示数据
    class CounterUI extends Component {
        render() {
            // 数据来源于 props
            const {value, handleIncrement, handleDecrement} = this.props;
            return (
                <div>
                    <p>state: {value}</p>
                    <button onClick={handleIncrement}>+1</button>
                    <button onClick={handleDecrement}>-1</button>
                </div>
            );
        }
    }
    // 定义一个 容器组件,用于与 Redux store 交互
    // 将 Redux state 按需注入到 UI 组件的 props
    function mapStateToProps(state) {
        return { value: state.value }
    }
      
    // 将 Redux actions 按需注入到 UI 组件的 props
    function mapDispatchToProps(dispatch) {
        return {
            handleIncrement: () => dispatch({type: 'INCREMENT'}),
            handleDecrement: () => dispatch({type: 'DECREMENT'})
        }
    }
    // 使用 connect 方法基于 UI组件 生成一个 容器组件
    const CounterContainer = connect(mapStateToProps, mapDispatchToProps)(CounterUI);
    
    const store = createStore(reducer);
    
    // 使用 Provider 作为根组件,使得所有子组件都能访问到 store
    ReactDOM.render(
        <Provider store={store}>
          <CounterContainer />
        </Provider>,
        document.getElementById('root')
    )
    

    4. 小结

    Redux 和 React-Redux 专门设计了约束和约定,导致代码量不见得减少,甚至还可能增加了。简单的项目看不出有什么优势,但在构建一定规模的企业级项目时,这些约束和条条框框,会有利于项目的维护和管理。正如 Java,有些人认为过于啰嗦,但这正是它严谨的体现!

    下一篇将会结合 antd UI框架模仿实现官方提供的购物车实例。

    下一篇:没有了