zoukankan      html  css  js  c++  java
  • React-Redux 实现伪代码

    前言

    React 相信大家都有过接触,React 一套MVC框架,根据 React 的框架诞生的一套使用方便的 处理数据状态的插件,redux。redux 也并不是就只是单纯使用于React , Vue 同样适用,它只是解决了一种处理数据的方式。

    一、Redux

    • 1.1 设计思想
      Redux 将组价中的状态,数据存储到顶层 Store 里,通过 provider 从顶层注入。
      组件通过 dispatch 触发 action, action 去覆盖之前的 reducer 中的 state 值。
      此时 store 值做到了更替,所以传入组件中的 state 值修改,页面 View 更新。



    • 1.2 三大原则

    整个应用用只有一个store, 其内部state tree 存储整个应用 state。
    修改数据通过 action 去派发。
    单一数据源让 react 组件之间的 数据通信简单方便,更有利于管理。

    • 1.3 createStore
    • 1.3.1 store
         import { create } from 'react';
                
         function reducer() {}
         let store = createStore(reducer)
    

    通过 creatstore 创建一个 store, 创建的store 有以下几个方法:

           * store.getState()  // 获取最新state
           * store.dispatch()  // 触发 aciton
           * store.subscribe() // 订阅 store 中 state 变化 
    
    • 1.3.2 reducer

    reducer 是一个纯函数,作用只是接收数据,数据覆盖,其他什么也不操作,state 值替换作用。

          const reducer = (state={count: 0}, action) => {
               switch (action.type) {
                    case ADD:
                         return { count: state.count + 1 }
                    case MINUS: 
                         return { count: state.count - 1 }
                    default: 
                         return state;
               }
          }
    
    • 1.3.3 getState、dispatch、subscribe
         import React from 'react'
         import store from '../store'
    
          class Counter extends React.Component{
              constructor(props){
                    super(props)
                    this.state = {
                      number: store.getState().count
              }
            }
            render () {
                    return <div>
                      <p>{this.state.number}</p>
                      <button onClick={() => store.dispatch({type: 'ADD'})}>+</button>
                      <button onClick={() => store.dispatch({type: 'MINUS'})}>-</button>
                    </div>
            }
          }
    
          export default Counter
          ```
    可以看到,reducer 函数中已经接受到了 action, 此时 store 中的 state 已经发生了变化,而页面不更新的原因在于 Counter 没有订阅 store 中 state 的变化,可在代码中加入下面代码
    
    ```jsx
    class Counter extends React.Component{
      componentDidMount () {
        this.unSubscribe = store.subscribe(() => {
          this.setState({
            number: store.getState().count
          })
        })
      }
      componentWillUnmount () {
        this.unSubscribe && this.unSubscribe()
      }
    }
    

    使用 store.subscribe 就可实现订阅,该方法接受一函数,当 store 中 state 中状态发生变化,就会执行传入的函数,同时 store.subscribe 方法返回一个函数,用于取消订阅。
    至此,Counter组件已基本实现了。可能有些小伙伴发现应用首次加载后,控制台输出了

    • 1.3.4 createStore 伪代码
    
    function createStore(reducer){
    	let state
      const listeners = []
      
      // 返回最新的 state
      function getState () {
      	return state
      }
      
      // 派发 action
      function dispatch(action){
      	state = reducer(state, action)
        listeners.forEach(listener => listener())
      }
      
      // 订阅,返回取消订阅函数
      function subscribe(listener){
      	listeners.push(listener)
        return function () {
        	const index = listeners.indexOf(listener)
          listeners.splice(index, 1)
        }
      }
      
      // 获取state默认值
      dispatch({type: "@@redux/INIT1.s.m.m.c.n"})
      
      // 返回 store, 一个对象
      return {
      	getState,
        dispatch,
        subscribe
      }
    }
    
    export default createStore
    
    
    • 1.4 combineReducers
      • 1.4.1 原理及使用

        当一个应用包含多个模块,将所以模块的 state 放在并不合理,更好的做法是按照模块进行划分,每个模块有各自的 reducer、action,最终通过 Redux 中的 combineReducers 合并成一个大的 reducer.

    // srcstore
    educersindex.js
    import {combineReducers} from 'redux';
    import counter1 from './counterReducer1';
    import counter2 from './counterReducer2';
    export default combineReducers({
        x: counter1,
        y: counter2
    });
    
    // src/store/reducers/counterReducer1.js
    import * as types from '../action-types';
    export default function (state= {count: 0},action){
        switch(action.type){
            case types.ADD1:
                return state.count + 1;
            case types.MINUS1:
                return state.count - 1;
            default:
                return state;
        }
    }
    
    // src/store/reducers/counterReducer2.js
    import * as types from '../action-types';
    export default function (state= {count: 0},action){
        switch(action.type){
            case types.ADD2:
                return state.count + 1;
            case types.MINUS2:
                return state.count - 1;
            default:
                return state;
        }
    }
    
    
    
    • 1.4.2 combineReducers 伪代码
    function combineReducers(reducers){
      // 返回合并之后的 reducer 函数
      return function (state, action){
      	const nextState = {}
        Object.keys(reducers).forEach(key => {
        	nextState[key] = reducers[key](state[key], action)
        })
        return nextState
      }
    }
    
    
    
    • 1.5 总结

    可以看出,在 React 组件中使用 store, 都需要手动去引入 store 文件, 手动订阅 store 中状态的变化,这是不合理的,接下来,我们看下 react-redux 是如何解决的

    二、React-Redux

    • 2.1 原理及使用
      react-redux 提供一个 Provider 组件,通过 Provider 组件,可以向其子组件、孙组件传递 store, 而不需要每个组件都手动引入
    import { Provider } from 'react-redux'
    import store from './store'
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    

    在后代组件 Counter1 中,可使用 react-redux 提供 connect 函数,将 store 与 Counter1 组件的 props 进行关联

    import React from 'react'
    import { connect } from 'react-redux'
    import action from '../store/actions/Counter1'
    
    class Counter1 extends React.Component{
      render () {
        return <div>
          <p>{ this.props.count }</p>
          <button onClick={ this.props.add }>+</button>
          <button onClick={ this.props.minus }>-</button>
        </div>
      }
    }
    
    const mapStateToProps = state => state
    const mapDispatchToProps = {
      ...action
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(Counter1)
    

    从上面代码中,可以看出在 Counter1 组件内部,属性或方法都是通过 props 访问,我们完全可以将 Counter1 组件转换成函数组件(无状态组件),通过函数组件外部都是一个容器组件(有状态组件)进行包裹,所有 connect(mapStateToProps, mapDispatchToProps)(Counter1) 最终返回的就是一个容器组件,接下来我们看下如何手写一个 react-redux。

    • 2.2 react-redux 伪代码

    创建一个 ReactReduxContext 上下文对象

    import React from 'react'
    export const ReactReduxContext = React.createContext(null)
    export default ReactReduxContext
    

    在 Proveider 组件中,需要使用 ReactReduxContext 对象中提供的 Provider 组件

    // src/react-redux/Provider.js
    import React from 'react'
    import {ReactReduxContext} from './Context'
    
    class Provider extends React.Component{
      constructor(props) {
      	super(props)
      }
      render () {
      	return <ReactReduxContext.Provider value={{ store: this.props.store }}>
          {this.props.children}
        </ReactReduxContext.Provider>
      }
    }
    export default Provider
    

    而 connect 方法,接收 mapStateToProps, mapDispatchToProps 两个参数,返回一个函数,返回的函数接收 自定义组件(例如 Counter1 ),函数执行后,返回最终的容器组件

    // src/react-redux/connect.js
    import React from 'react'
    import {bindActionCreators} from 'redux'
    import {ReactReduxContext} from './Context'
    
    function connect(mapStateToProps, mapDispatchToProps) {
      return function (WrappedComponent) {
        // 返回最终的容器组件
         return class extends React.Component{
        	static contextType = ReactReduxContext
        	constructor(props, context){
          	  super(props)
              this.state = mapStateToProps(context.store.getState())
            }
        	shouldComponentUpdate() {
              if (this.state === mapStateToProps(this.context.store.getState())) {
                return false;
              }
              return true;
            }
        	componentDidMount () {
          	  this.unsubscribe = this.context.subscribe(() => {
            	this.setState(mapStateToProps(this.context.store.getState()))
              })
            }
        	componentWillUnmount (){
          	  this.unsubscribe && this.unsubscribe()
            }
            render(){
              const actions = bindActionCreators(
                mapDispatchToProps,
                this.context.store.dispatch
              )
          	  return <WrappedComponent {...this.state} {...this.props} {...actions}/>
           }
         }
       }
    }
    
    export default connect
    
    

    可以看出,connect 方法中,有 bindActionCreators 绑定 action 与 store.dispatch, 有订阅 store 中的 state 变化,这些都是我们只使用 redux ,需要在 react 组件中需要手动去写的,幸运的是,现在 react-redux 帮我们去干了.

    总结

    redux 主要应用在项目中的状态变化存储,组件之间的数据共享,当数据需要使用时,不存在层层传递到当前组件问题, 实现了类似于数据的传送门,哪里使用哪里取得。希望对您有所帮助吧。

    点击关注,谢谢您的点赞~~~

  • 相关阅读:
    Win7系统下搭建Tomcat服务器
    转:在线框架引用 bootstrap/jq/jqmobile/css框架
    转:Web.config配置文件详解
    转:HTML和Web窗体的区别
    在VS2010中创建网站并发布
    nohup 借助cronolog进行日志分割 Linux
    Linux (centos 8) 安装nginx1.20
    Ubuntu 安装使用yum--转载
    C# 字符串插值内部工作原理
    Linux 安装MySQL
  • 原文地址:https://www.cnblogs.com/GongYaLei/p/14188517.html
Copyright © 2011-2022 走看看