2021-02-01 React-redux的API设计与实现

申自明
2023-12-01

Hooks API
useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

其中init是个函数处理initialArg参数的
useReducer是useState的替代方案。它接收一个形如(state, action)=>newState的reducer,并返回当前的state以及配套的dispatch方法

useEffect

useEffect(didUpdate)

该Hook接收一个包含命令式、且可能有副作用代码的函数。在函数组件主体内(这里指在React渲染阶段)改变DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的bug并破坏UI的一致性
使用useEffect完成副作用操作。赋值给useEffect的函数会在组件渲染到屏幕之后延迟执行。默认情况下,effect将在每轮渲染结束后执行,但也可以选在让它在只有某些值凡改变的时候才执行

useLayoutEffect
其函数签名与useEffect相同,但它会在所有的DOM变更之后同步调用effect。可以使用它来读取DOM布局并触发同步渲染。在浏览器执行绘制之前,useLayoutEffect内部的更新计划将被同步刷新。尽可能使用标准的useEffect以避免阻塞视觉更新

useMemo

const memorizedValue = useMemo(()=>compuedValue(a,b), [a,b]);

返回一个memorized值,指缓存过的值。把创建函数和依赖项作为参数传入useMemo,它仅会在某个依赖项改变时才重新计算memorized的值。这种优化有助于避免每次渲染时都进行高开销的计算。传入的useMemo的函数会在渲染期间执行,不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于useEffect的范畴,而不是useMemo。如果没有提供依赖项数组,useMemo会在每次渲染时都会重新计算新的值

useCallback

const memoizedCallback = useCallback(()=>{dosomething(a,b)}, [a,b])

返回一个memoized回调函数,就是缓存过的函数
把内联回调函数及依赖项数组作为参数传入useCallback,它将返回该回调函数的memoized版本,该回调函数仅在某个依赖项改变时才会更新
useCallback(fn, deps)相当于useMemo(()=>fn, deps)

React-redux
在使用redux的时候,每次都要取值都要调用getState方法,渲染时时候订阅subscribe方法,太low了,用更react的方式来写,就需要react-redux的支持

react-redux提供了两个api
1、Provider为后代组件提供store
2、connect为组件提供数据和变更方法

Provider的用法:

import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import store from "./store";
import { Provider } from "react-redux";

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

connect的用法:

import React, { Component } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";

class ReactReduxPage extends Component {
  render() {
    console.log("props", this.props);
    const { count, dispatch, add, minus } = this.props;
    return (
      <div>
        <h3>ReactReduxPage</h3>
        <p>{count}</p>
        <button onClick={() => dispatch({ type: "ADD", payload: 15 })}>
          dispatch add
        </button>
        <button onClick={add}>add</button>
        <button onClick={minus}>minus</button>
      </div>
    );
  }
}

// hoc是个函数,接收组件作为参数,返回一个新的组件
// connect原理 高阶组件(hoc)
// mapStateToProps(function)把state map(映射) props上一份
const mapStateToProps =
  // ({ count }) => ({ count }); // 这是对象解构
  state => { return { count: state.count } };

// mapDispatchToProps(function|object)把方法映射到 props上一份
const mapDispatchToProps =
  /* {
  // object,不传参默认dispatch
  add: () => ({ type: "ADD", payload: 12 }),
  minus: () => ({ type: "MINUS", payload: 2 })
}; */
  // function
  dispatch => {
    let creators = {
      add: () => ({ type: "ADD", payload: 12 }),
      minus: () => ({ type: "MINUS", payload: 2 })
    };
    creators = bindActionCreators(creators, dispatch);
    return { dispatch, ...creators };
  };

export default connect(mapStateToProps, mapDispatchToProps)(ReactReduxPage);

useDispatch&useSelector:react-redux两个hooks API使用方式

import React, { useCallback } from "react";
// import { useDispatch, useSelector } from "react-redux";
import { useDispatch, useSelector } from "../store/kreactredux/kReactRedux";

function ReactReduxHookPage() {
  const dispatch = useDispatch(); // useDispatch获取dispatch

  const add = useCallback(() => {
    dispatch({ type: "ADD", payload: 2 });
    // eslint-disable-next-line
  }, []);
  const count = useSelector(({ count }) => count); // useSelector获取store state
  return (
    <div>
      <h3>ReactReduxHookPage</h3>
      <p>{count}</p>
      <button onClick={add}>add</button>
    </div>
  );
}
export default ReactReduxHookPage;

React-redux完整API实现:

import React, { useCallback, useContext, useLayoutEffect } from "react";

// Provider API
// React跨层级数据传递 context
// 1.创建context对象
const Context = React.createContext();

// 2.Provider传递value
export function Provider({ store, children }) {
  return <Context.Provider value={store}>{children}</Context.Provider>;
}

// 3.子组件消费context value
// 消费方式有三种:
// contextType(只能用在类组件,且订阅单一的context来源)
// useContext(只能用在函数组件中以及自定义hook中)
// consumer(没什么限制,只是步骤繁琐)
export const connect = (
  mapStateToProps, // function
  mapDispatchToProps // object|function
) => WrappedComponent => props => {
  // 获取store
  const store = useStore();
  const { getState, dispatch, subscribe } = store;
  const stateProps = mapStateToProps(getState());
  let dispatchProps = { dispatch }; // 默认
  if (typeof mapDispatchToProps === "function") {
    dispatchProps = mapDispatchToProps(dispatch);
  } else if (typeof mapDispatchToProps === "object") {
    dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
  }

  // 订阅state的变更
  // 函数组件的forceUpdate
  // const [, forceUpdate] = React.useReducer(x => x + 1, 0);
  // const [, forceUpdate] = React.useState({});
  const forceUpdate = useForceUpdate();

  // useEffect DOM更新 effect延迟执行
  // useLayoutEffect DOM更新 effect立马执行
  useLayoutEffect(() => {
    const unsubscribe = subscribe(() => {
      // 如果state改变,则更新组件
      forceUpdate();
    });
    return () => {
      // 组件将要卸载之前取消订阅
      unsubscribe();
    };
    // eslint-disable-next-line
  }, [store]);

  return <WrappedComponent {...props} {...stateProps} {...dispatchProps} />;
};

export function bindActionCreators(creators, dispatch) {
  let obj = {};

  for (let key in creators) {
    obj[key] = bindActionCreator(creators[key], dispatch);
  }

  return obj;
}

// bindActionCreators API
function bindActionCreator(creator, dispatch) {
  // 保存redux状态管理库的状态,dispatch(action)
  return (...args) => dispatch(creator(...args));
}

// 方法1:const [, forceUpdate] = React.useReducer(x => x + 1, 0);
// 方法2:const [, forceUpdate] = React.useState({});
// 方法3:自定义hooks-forceUpdate
function useForceUpdate() {
  /*eslint-disable-next-line */
  const [state, setState] = React.useState(0);
  // 使用useCallback做缓存函数
  const update = useCallback(() => setState(prev => prev + 1), []);
  return update;
}

// hooks

// set
export function useDispatch() {
  const { dispatch } = useStore();
  return dispatch;
}

// get
export function useSelector(selector) {
  const store = useStore();
  const { getState, subscribe } = store;
  const selectorState = selector(getState());

  // 订阅更新
  const forceUpdate = useForceUpdate();
  useLayoutEffect(() => {
    const unsubscribe = subscribe(() => {
      forceUpdate();
    });
    return () => {
      unsubscribe();
    };
    // eslint-disable-next-line
  }, [store]);

  return selectorState;
}

function useStore() {
  const store = useContext(Context);
  return store;
}

 类似资料: