美文网首页
4. Redux是啥?

4. Redux是啥?

作者: Jason_Shu | 来源:发表于2019-01-28 11:57 被阅读0次

    我们用「原生JS + Redux」来完成一个「Counter」的小需求。

    在上一节中(https://www.jianshu.com/p/ff0bfe9a0981),我们已经用过「Redux」改写过需求。今天我们再来剖析下「Redux」。

    JS代码:

    let createStore = Redux.createStore;
    
    let store = createStore(stateChanger);
    
    
    // stateChanger函数里面,集合了所有「数据修改」操作
    function stateChanger(state, action) {
      if(typeof state === "undefined") {
        state = 0;
      }
      switch(action.type) {
        case 'ADD':
          return state = state + action.payload;
        case 'MINUS':
          return state = state - action.payload;
        default:
          return state;
      }
    }
    
    
    function render() {
      let app = document.querySelector('#root');
      app.innerHTML = `
        <div>
          <span class="number">${store.getState()}</span>
          <button id="add" onclick="add()">+</button>
          <button id="minus" onclick="minus()">-</button>
          <button id="addOdd" onclick="addOdd()">单数+2</button>
          <button id="addAsync" onclick="addAsync()">1秒后+2</button>
        </div>
      `;
    }
    
    function add() {
      // dispatch相当于发送一个action,然后根据「stateChanger」
      // 里面的类型,进行数据更新
      store.dispatch({type: 'ADD', payload: 1});
    }
    
    function minus() {
      store.dispatch({type: 'MINUS', payload: 1});
    }
    
    function addOdd() {
      if(store.getState() % 2 === 1) {
        store.dispatch({type: 'ADD', payload: 2});
      }
    }
    
    function addAsync() {
     setTimeout(() => {
       store.dispatch({type: 'ADD', payload: 2});
     }, 1000)
    }
    
    
    
    
    render();
    
    // 监听store,如果store发生变化,则执行回调函数。
    store.subscribe(() => {
      render();
    });
    

    总结下上述代码,「Redux」会多几个术语,分别为「store,stateChanger,dispatch,subscriber」。

    • store,顾名思义,就是所有数据的集合。
    • stateChanger,一般也称为「reducer」,就是所有「更新数据的行为」的集合。
    • dispatch,可以理解为发送action,然后根据「stateChanger」里面的action type来更新「store」里面的数据。
    • payload, 就是action里面的data值。
    • subscriber,监听「store」,一旦「store」里面的数据有变动,就会执行回调函数,一般这个回调函数就是render函数,来重新渲染需要更新的页面。

    接下来我们进入「第二阶段」,运用「React」和「Redux」来再次实现上述需求。

    我们需要装一个包「create-react-app」,可以为我们搭建好一个React环境。同时记得装「Redux」。都安装好后,项目结构如下:


    image.png

    我们只操作「App.js」和「index.js」文件。

    App.js

    import React, { Component } from 'react';
    
    class App extends Component {
    
      add() {
        this.props.store.dispatch({type: 'ADD', payload: 1});
      }
    
      minus() {
        this.props.store.dispatch({type: 'MINUS', payload: 1});
      }
    
      addOdd() {
        if(this.props.store.getState() % 2 === 1) {
          this.props.store.dispatch({type: 'ADD', payload: 2});
        }
      }
    
      addAsync() {
        setTimeout(() => {
          this.props.store.dispatch({type: 'ADD', payload: 1});
        }, 1000);
      }
    
      render() {
        return (
          <div className="App">
            <span className="number">{this.props.store.getState()}</span>
            <button id="add" onClick={() => this.add()}>+</button>
            <button id="minus" onClick={() => this.minus()}>-</button>
            <button id="addOdd" onClick={() => this.addOdd()}>单数+2</button>
            <button id="addAsync" onClick={() => this.addAsync()}>1秒后+2</button>
          </div>
        );
      }
    }
    
    export default App;
    

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import * as serviceWorker from './serviceWorker';
    import {createStore} from 'redux';
    
    let store = createStore(stateChanger);
    
    function stateChanger(state, action) {
        if(typeof state === "undefined") {
            state = 0;
        }
        switch(action.type) {
            case 'ADD':
                return  state = state + action.payload;
            case 'MINUS':
                return  state = state - action.payload;
            default:
                return state;
        }
    }
    
    
    render();
    
    store.subscribe(() => {
        render();
    })
    
    function render() {
        ReactDOM.render(<App store={store}/>, document.getElementById('root'));
    
    }
    
    // If you want your app to work offline and load faster, you can change
    // unregister() to register() below. Note this comes with some pitfalls.
    // Learn more about service workers: http://bit.ly/CRA-PWA
    serviceWorker.unregister();
    
    

    我们换了「React+Redux」方案后,可以打开「开发者工具」,看到最显著的变化就是,这样只会局部更新。更新需要改变的节点。

    但是也产生一个问题<App/>的子组件中,如果要用类似「store」的属性,必须一层层传下去,然后通过「props」获取,像「传家宝」一样。。。那有没有什么办法,使得每层直接获取「store」不用每层传递呢?

    我们进入第三阶段,引用「React-Redux」。同样只是修改「App.js」和「index.js」。

    App.js

    import React, { Component } from 'react';
    
    import { connect } from 'react-redux';
    
    
    class App extends Component {
    
    
      addOdd() {
        if(this.props.number % 2 === 1) {
          this.props.addOdd()
        }
      }
    
      addAsync() {
        setTimeout(() => {
          this.props.addAsync();
        }, 1000)
      }
    
      render() {
        return (
          <div className="App">
            <span className="number">{this.props.number}</span>
            <button id="add" onClick={() => this.props.add()}>+</button>
            <button id="minus" onClick={() => this.props.minus()}>-</button>
            <button id="addOdd" onClick={() => this.addOdd()}>单数+2</button>
            <button id="addAsync" onClick={() => this.addAsync()}>1秒后+2</button>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        number: state.number
      }
    };
    
    
    // 注意「mapDispatchToProps」是一个对象
    const mapDispatchToProps =  {
      add: () => {
        return {
          type: 'ADD',
          payload: 1
        }
      },
      minus: () => {
        return {
          type: 'MINUS',
          payload: 1
        }
      },
      addOdd: () => {
        return {
          type: 'ADD',
          payload: 2
        }
      },
      addAsync: () => {
        return {
          type: 'ADD',
          payload: 2
        }
      }
    }
    
    
    export default connect(
        mapStateToProps,
        mapDispatchToProps
    )(App);
    
    

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import * as serviceWorker from './serviceWorker';
    import {createStore} from 'redux';
    
    import { Provider } from 'react-redux';
    
    
    let store = createStore(stateChanger);
    
    function stateChanger(state, action) {
        if(typeof state === "undefined") {
            return {
                number: 0
            }
        }
        switch(action.type) {
            case 'ADD':
                return  {
                    number: state.number + action.payload
                };
            case 'MINUS':
                return  {
                    number: state.number - action.payload
                };
            default:
                return state;
        }
    }
    
    
    render();
    
    store.subscribe(() => {
        render();
    })
    
    function render() {
        ReactDOM.render(
        <Provider store={store}>
            <App />
        </Provider>,
        document.getElementById('root'));
    
    }
    
    // If you want your app to work offline and load faster, you can change
    // unregister() to register() below. Note this comes with some pitfalls.
    // Learn more about service workers: http://bit.ly/CRA-PWA
    serviceWorker.unregister();
    
    

    通过一个「Provider」组件把「App」组件包裹住,然后把「store」放在「Provider」组件上。在「App」组件中,最后使用「connect」函数,第一个参数接受「mapStateToProps」和「mapDispatchToProps」。
    「mapStateToProps」的作用是是接受父组件传来的「store」并只获取其中部分属性,获得的部分属性供当前组件使用,当前组件可以通过「this.props」调用。
    「mapDispatchToProps」是一个「对象」,里面的每个属性就是action,当前组件可以通过「this.props」统一调用。

    我们可以log看看「this.props」。


    image.png

    大概流程就是,「App.js」中点击按钮,通过「mapDispatchToProps」,然后「index.js」中的「stateChanger」函数来更新「store」,「store」有更新就被「subscriber」监听到,然后重新渲染,固然传入「Provider」的「store」有改变,然后「App.js」中的「mapStateToProps」获取整个「store」的「部分属性」,供当前组件使用,可以使用「this.props」获取「属性值」和「方法」。

    相关文章

      网友评论

          本文标题:4. Redux是啥?

          本文链接:https://www.haomeiwen.com/subject/emiojqtx.html