• react-redux 实现原理


    摘自:https://juejin.im/post/5def4831e51d45584b585000?utm_source=gold_browser_extension

    redux 简单实现,一个简单的订阅发布机制。

    // reducer.js
    const initialState = {
      count: 0
    }
    export function reducer(state = initialState, action) {
      switch (action.type) {
        case 'plus':
          return {
            ...state,
            count: state.count + 1
          }
        case 'subtract':
          return {
            ...state,
            count: state.count - 1
          }
        default:
          return initialState
      }
    }
    
    // createStore.js
    import { reducer } from './reducer'
    export const createStore = (reducer) => {
      let currentState = {}
      let observers = []             //观察者队列        
      function getState() {
        return currentState
      }
      function dispatch(action) {
        currentState = reducer(currentState, action)
        observers.forEach(fn => fn())
      }
      function subscribe(fn) {
        observers.push(fn)
      }
      dispatch({ type: '@@REDUX_INIT' })  //初始化store数据        
      return { getState, subscribe, dispatch }
    }
    
    const store = createStore(reducer)       //创建store
    store.subscribe(() => { console.log('组件1收到store的通知') })
    store.subscribe(() => { console.log('组件2收到store的通知') })
    store.dispatch({ type: 'plus' })         //执行dispatch,触发store的通知
    

    react-redux

    若用 redux,一个组件如果想从store存取公用状态,需要进行四步操作:

    • import 引入 store
    • getState 获取状态
    • dispatch 修改状态
    • subscribe 订阅更新

    代码相对冗余,我们想要合并一些重复的操作,而 react-redux 就提供了一种合并操作的方案:react-redux 提供 Provider 和 connect 两个 API

    • Provider 将 store 放进 this.context 里,省去了 import 这一步
    • connect 将 getState、dispatch 合并进了this.props,并自动订阅更新,简化了另外三步。

    Provider

    import React from 'react'
    import PropTypes from 'prop-types'
    export class Provider extends React.Component {
      // 需要声明静态属性childContextTypes来指定context对象的属性,是context的固定写法  
      static childContextTypes = {
        store: PropTypes.object
      }
    
      // 实现getChildContext方法,返回context对象,也是固定写法  
      getChildContext() {
        return { store: this.store }
      }
    
      constructor(props, context) {
        super(props, context)
        this.store = props.store
      }
    
      // 渲染被Provider包裹的组件  
      render() {
        return this.props.children
      }
    }
    

    connect

    connect的调用方式:connect(mapStateToProps, mapDispatchToProps)(App)

    export function connect(mapStateToProps, mapDispatchToProps) {
      return function (Component) {
        class Connect extends React.Component {
          componentDidMount() {
            //从context获取store并订阅更新          
            this.context.store.subscribe(this.handleStoreChange.bind(this));
          }
          handleStoreChange() {
            // 触发更新          
            // 触发的方法有多种,这里为了简洁起见,直接forceUpdate强制更新,读者也可以通过setState来触发子组件更新          
            this.forceUpdate()
          }
          render() {
            return (
              <Component
                // 传入该组件的props,需要由connect这个高阶组件原样传回原组件              
                {...this.props}
                // 根据mapStateToProps把state挂到this.props上              
                {...mapStateToProps(this.context.store.getState())}
                // 根据mapDispatchToProps把dispatch(action)挂到this.props上              
                {...mapDispatchToProps(this.context.store.dispatch)}
              />
            )
          }
        }
        //接收context的固定写法      
        Connect.contextTypes = {
          store: PropTypes.object
        }
        return Connect
      }
    }
    

    这种实现其实更便于实现装饰器模式。

    完整例子

    写一个计数器,点击按钮就派发一个 dispatch,让 store 中的 count 加一,页面上显示这个 count

    // store.js
    export const createStore = (reducer) => {
      let currentState = {}
      let observers = []             //观察者队列    
      function getState() {
        return currentState
      }
      function dispatch(action) {
        currentState = reducer(currentState, action)
        observers.forEach(fn => fn())
      }
      function subscribe(fn) {
        observers.push(fn)
      }
      dispatch({ type: '@@REDUX_INIT' }) //初始化store数据    
      return { getState, subscribe, dispatch }
    }
    
    //reducer.js
    const initialState = {
      count: 0
    }
    
    export function reducer(state = initialState, action) {
      switch (action.type) {
        case 'plus':
          return {
            ...state,
            count: state.count + 1
          }
        case 'subtract':
          return {
            ...state,
            count: state.count - 1
          }
        default:
          return initialState
      }
    }
    
    //react-redux.js
    //react-redux.js
    import React from 'react'
    import PropTypes from 'prop-types'
    export class Provider extends React.Component {
      // 需要声明静态属性childContextTypes来指定context对象的属性,是context的固定写法  
      static childContextTypes = {
        store: PropTypes.object
      }
    
      // 实现getChildContext方法,返回context对象,也是固定写法  
      getChildContext() {
        return { store: this.store }
      }
    
      constructor(props, context) {
        super(props, context)
        this.store = props.store
      }
    
      // 渲染被Provider包裹的组件  
      render() {
        return this.props.children
      }
    }
    
    export function connect(mapStateToProps, mapDispatchToProps) {
      return function (Component) {
        class Connect extends React.Component {
          componentDidMount() {          //从context获取store并订阅更新          
            this.context.store.subscribe(this.handleStoreChange.bind(this));
          }
          handleStoreChange() {
            // 触发更新          
            // 触发的方法有多种,这里为了简洁起见,直接forceUpdate强制更新,读者也可以通过setState来触发子组件更新          
            this.forceUpdate()
          }
          render() {
            return (
              <Component
                // 传入该组件的props,需要由connect这个高阶组件原样传回原组件              
                {...this.props}
                // 根据mapStateToProps把state挂到this.props上              
                {...mapStateToProps(this.context.store.getState())}
                // 根据mapDispatchToProps把dispatch(action)挂到this.props上              
                {...mapDispatchToProps(this.context.store.dispatch)}
              />
            )
          }
        }
    
        //接收context的固定写法      
        Connect.contextTypes = {
          store: PropTypes.object
        }
        return Connect
      }
    }
    
    //index.js
    import React from 'react'
    import ReactDOM from 'react-dom'
    import App from './App'
    import { Provider } from './react-redux'
    import { createStore } from './store'
    import { reducer } from './reducer'
    
    ReactDOM.render(
      <Provider store={createStore(reducer)}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    //App.js
    import React from 'react'
    import { connect } from './react-redux'
    
    const addCountAction = {
      type: 'plus'
    }
    
    const mapStateToProps = state => {
      return {
        count: state.count
      }
    }
    
    const mapDispatchToProps = dispatch => {
      return {
        addCount: () => {
          dispatch(addCountAction)
        }
      }
    }
    
    class App extends React.Component {
      render() {
        return (
          <div className="App">
            {this.props.count}
            <button onClick={() => this.props.addCount()}>增加</button>
          </div>
        );
      }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(App)
    
  • 相关阅读:
    kafka报错:Invalid message size: 0
    转载:elastic5.x部署常见问题总结
    hadoop集群zookeeper迁移
    生产环境轻量级dns服务器dnsmasq搭建文档
    (3)安装elastic6.1.3及插件kibana,x-pack,essql,head,bigdesk,cerebro,ik
    (2)安装elastic6.1.3及插件kibana,x-pack,essql,head,bigdesk,cerebro,ik
    (1)安装elastic6.1.3及插件kibana,x-pack,essql,head,bigdesk,cerebro,ik
    换种思路解决日志占用磁盘空间问题
    更改hadoop集群yarn的webui中的开始时间和结束时间为本地时间
    两种虚拟机扩容方式扩容后在线生效的方法
  • 原文地址:https://www.cnblogs.com/everlose/p/12516520.html
Copyright © 2020-2023  润新知