Redux Hooks

Redux Hooks

在函数式组件中如果希望使用Redux,我们可以使用connect函数注入StateAction Creator,也可以使用React Redux提供的Hooks Api

useSelector

const result : any = useSelector(selector : Function, equalityFn? : Function)

useSelector允许我们通过传入的selector函数将State中数据提取出来,其相当于connect中的mapStateToProps的函数。该selector会在组件重渲染时候被调用,useSelector同样会监听Redux store的变化,然后在某个action分发时调用。

当某个action被分发时,useSelector会对之前selector返回的结果与当前的结果进行对比;当发现值不同时,该组件会被强制重渲染。useSelector值会使用严格比较(===)来判断值的变化,而connect函数会使用浅比较(==)来判断是否需要进行重渲染。在mapState中,所有指定的返回域会被合并为某个对象,connect会自动去比较单个属性值是否发生变化。而useSelector中则是会直接比较selector函数的返回值;。

import React from "react";
import { useSelector } from "react-redux";

export const CounterComponent = () => {
  const counter = useSelector(state => state.counter);
  return <div>{counter}</div>;
};

// 如果需要引用 Props 中的数据,则以闭包方式传入
export const TodoListItem = props => {
  const todo = useSelector(state => state.todos[props.id]);
  return <div>{todo.text}</div>;
};

在上述的用法中,每次组件渲染的时候都会创建新的selector函数实例;我们可以使用reselect来创建可缓存的selector函数:

import React from "react";
import { useSelector } from "react-redux";
import { createSelector } from "reselect";

const selectNumOfDoneTodos = createSelector(
  state => state.todos,
  todos => todos.filter(todo => todo.isDone).length
);

export const DoneTodosCounter = () => {
  const NumOfDoneTodos = useSelector(selectNumOfDoneTodos);
  return <div>{NumOfDoneTodos}</div>;
};

export const App = () => {
  return (
    <>
      <span>Number of done todos:</span>
      <DoneTodosCounter />
    </>
  );
};

useDispatch

const dispatch = useDispatch();

Hook会返回Redux store中的dispatch函数的引用,可以永安里分发Action

import React from "react";
import { useDispatch } from "react-redux";

export const CounterComponent = ({ value }) => {
  const dispatch = useDispatch();

  return (
    <div>
      <span>{value}</span>
      <button onClick={() => dispatch({ type: "increment-counter" })}>
        Increment counter
      </button>
    </div>
  );
};

当我们在父组件封装某个事件处理函数时,建议是使用useCallback来创建缓存的函数,以避免子组件因为事件处理函数的变化而造成的无意义渲染:

import React, { useCallback } from "react";
import { useDispatch } from "react-redux";

export const CounterComponent = ({ value }) => {
  const dispatch = useDispatch();
  const incrementCounter = useCallback(
    () => dispatch({ type: "increment-counter" }),
    [dispatch]
  );

  return (
    <div>
      <span>{value}</span>
      <MyIncrementButton onIncrement={incrementCounter} />
    </div>
  );
};

export const MyIncrementButton = React.memo(({ onIncrement }) => (
  <button onClick={onIncrement}>Increment counter</button>
));

useStore

const store = useStore();

Hook会返回Provider中传入的store实例:

import React from "react";
import { useStore } from "react-redux";

export const CounterComponent = ({ value }) => {
  const store = useStore();

  // EXAMPLE ONLY! Do not do this in a real app.
  // The component will not automatically update if the store state changes
  return <div>{store.getState()}</div>;
};
下一页