React-Redux 原理

React | 2020-06-25 13:23:51 592次 8次

hooks 出现之前,通常使用高阶组件的方式,hooks 之后不再使用高阶组件的方式,但是原理仍然是依据 context


一、React-Redux原理

高阶组件创建:

connect.js

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

export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
  class Connect extends Component {
    static contextTypes = {
      store: PropTypes.object
    }
    constructor () {
      super()
      this.state = { 
        allProps: {} 
      }
    }
    componentWillMount () {
      const { store } = this.context
      this._updateProps()
      store.subscribe(() => this._updateProps())
    }

    _updateProps () {
      const { store } = this.context
      let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) : {}
      //dispatch
      let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {} 
      this.setState({
        allProps: { 
          // 整合普通的 props 和从 state 生成的 props
          ...stateProps,
          ...dispatchProps,
          ...this.props
        }
      })
    }
    render () {
      return <WrappedComponent {...this.state.allProps} />
    }
  }

  return Connect
}
provider.js

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

export class Provider extends Component {
  static propTypes = {
    children: PropTypes.any
  }

  static childContextTypes = {
    store: PropTypes.object
  }

  getChildContext () {
    return {
      store: this.props.store
    }
  }

  render () {
    return (
      <div>{this.props.children}</div>
    )
  }
}



二、Hooks-Redux 原理

使用:

import React, { useState } from 'react';
import { Provider, useSelector, useDispatch } from '../src';
import { createStore } from 'redux';

// store定义
export const store = createStore(reducer);

function Count() {
  // 使用 useSelector 获取状态
  const count = useSelector((state) => state.count);
  // dispatch 方法定义获取
  const dispatch = useDispatch();
  
  const add = () => {
      dispatch({ type: 'add' })
  }

  return (
    <Button onClick={add}>add+</Button>
  );
}

export default () => {
  return (
    <Provider store = { store }>
      <Count />
    </Provider>
  );
};

使用方式上不用 connect 进行组件包裹,使用起来也比较清爽,下面看下简单实现

1、Context 实现

import React, { useContext } from 'react';

export const Context = React.createContext(null);

export function useReduxContext() {
  const contextValue = useContext(Context);

  if (!contextValue) {
    throw new Error(
      'could not find react-redux context value; please ensure the component is wrapped in a <Provider>',
    );
  }

  return contextValue;
}

2、Provider 实现

import React from 'react';
import { Context } from './Context';

export const Provider = ({ store, children }) => {
  return <Context.Provider value={{ store }}>
      {children}
  </Context.Provider>;
};

3、useDispatch 实现

import { useReduxContext } from './Context';

export function useDispatch() {
  const { store } = useReduxContext();
  return store.dispatch;
}

4、useSelector 实现

import { useReducer, useRef, useEffect } from 'react';
import { useReduxContext } from './Context';

// 默认比较的方法
const defaultEqualityFn = (a, b) => a === b;

export function useSelector(
  selector,
  equalityFn,
) {
  const { store } = useReduxContext();
  
  // 强制让当前组件渲染的方法。
  const [, forceRender] = useReducer(s => s + 1, 0);

  // 存储上一次selector的返回值。
  const latestSelectedState = useRef();
  
  // 根据用户传入的selector,从store中拿到用户想要的值
  const selectedState = selector(store.getState());

  // 检查是否需要强制更新
  function checkForUpdates() {
    // 从store中拿到最新的值
    const newSelectedState = selector(store.getState());

    // 如果比较相等不渲染
    if (equalityFn(newSelectedState, latestSelectedState.current)) {
      return;
    }
    
    // 强制渲染
    latestSelectedState.current = newSelectedState;
    forceRender();
  }
  
  // 组件第一次渲染后 执行订阅store的逻辑
  useEffect(() => {
    //  订阅redux store的变化,这里仍然使用 redux 提供的能力
    // 在用户调用了dispatch后,执行checkForUpdates
    const unsubscribe = store.subscribe(checkForUpdates);
    
    // 组件被销毁 取消订阅
    return unsubscribe;
  }, []);
  
  return selectedState;
}

8人赞

分享到: