use*_*757 15 javascript reactjs redux react-redux react-hooks
我正在使用带有钩子的 react-redux,我需要一个选择器,它采用的参数不是 prop。该文件指出
选择器函数不接收 ownProps 参数。但是,可以通过闭包(参见下面的示例)或使用柯里化选择器来使用 props。
但是,他们没有提供示例。如文档中所述,咖喱的正确方法是什么?
这就是我所做的,它似乎有效,但这是对的吗?从函数返回函数是否有影响useSelector(它似乎永远不会重新渲染?)
// selectors
export const getTodoById = state => id => {
let t = state.todo.byId[id];
// add display name to todo object
return { ...t, display: getFancyDisplayName(t) };
};
const getFancyDisplayName = t => `${t.id}: ${t.title}`;
// example component
const TodoComponent = () => {
// get id from react-router in URL
const id = match.params.id && decodeURIComponent(match.params.id);
const todo = useSelector(getTodoById)(id);
return <span>todo.display</span>;
}
Run Code Online (Sandbox Code Playgroud)
Emi*_*ron 16
When the return value of a selector is a new function, the component will always re-render on each store change.
useSelector()===默认情况下使用严格的引用相等检查,而不是浅相等
您可以使用超级简单的选择器来验证这一点:
const curriedSelector = state => () => 0;
let renders = 0;
const Component = () => {
// Returns a new function each time
// triggers a new render each time
const value = useSelector(curriedSelector)();
return `Value ${value} (render: ${++renders})`;
}
Run Code Online (Sandbox Code Playgroud)
即使value是 always 0,组件也会在每个 store 操作上重新渲染,因为useSelector它不知道我们正在调用该函数来获取实际值。
但是,如果我们确保useSelector接收的是 finalvalue而不是函数,那么组件只会在实际value更改时呈现。
const curriedSelector = state => () => 0;
let renders = 0;
const Component = () => {
// Returns a computed value
// triggers a new render only if the value changed
const value = useSelector(state => curriedSelector(state)());
return `Value ${value} (render: ${++renders})`;
}
Run Code Online (Sandbox Code Playgroud)
结论是它可以工作,但是从每次调用时使用的选择器返回一个新函数(或任何新的非原语)是非常低效的useSelector。
props 可以通过闭包使用(见下面的例子)或使用柯里化选择器。
该文档意味着:
useSelector(state => state.todos[props.id])useSelector(state => curriedSelector(state)(props.id))connect 总是可用的,如果你稍微改变你的选择器,它可以同时使用。
export const getTodoById = (state, { id }) => /* */
const Component = props => {
const todo = useSelector(state => getTodoById(state, props));
}
// or
connect(getTodoById)(Component)
Run Code Online (Sandbox Code Playgroud)
请注意,由于您要从选择器返回一个 Object,您可能希望将默认的相等检查更改useSelector为浅层相等检查。
Run Code Online (Sandbox Code Playgroud)import { shallowEqual } from 'react-redux' export function useShallowEqualSelector(selector) { return useSelector(selector, shallowEqual) }
要不就
const todo = useSelector(state => getTodoById(state, id), shallowEqual);
Run Code Online (Sandbox Code Playgroud)
如果您在选择器中执行昂贵的计算,或者数据嵌套很深并且性能成为问题,请查看Olivier 使用 memoization 的答案。
这是一个解决方案,它使用memoïzation不会在每次商店更改时重新渲染组件:
首先我创建一个函数来制作选择器,因为选择器依赖于组件属性id,所以我希望每个组件实例都有一个新的选择器。
当 todo 或 id 属性没有改变时,选择器将阻止组件重新渲染。
最后我使用,useMemo因为我不想每个组件实例有一个以上的选择器。
// selectors
const makeGetTodoByIdSelector = () => createSelector(
state => state.todo.byId,
(_, id) => id,
(todoById, id) => ({
...todoById[id],
display: getFancyDisplayName(todoById[id])
})
);
const getFancyDisplayName = t => `${t.id}: ${t.title}`;
// example component
const TodoComponent = () => {
// get id from react-router in URL
const id = match.params.id && decodeURIComponent(match.params.id);
const getTodoByIdSelector = useMemo(makeGetTodoByIdSelector, []);
const todo = useSelector(state => getTodoByIdSelector(state, id));
return <span>todo.display</span>;
}
Run Code Online (Sandbox Code Playgroud)