Redux Hooks
Redux Hooks
在函数式组件中如果希望使用 Redux,我们可以使用 connect 函数注入 State 与 Action 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>;
};