React | 2020-06-25 13:23:51 777次 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人赞