reactjs - 如何使用反应钩子在全局状态的叶值更改时有条件地渲染组件
问题描述
我正在尝试创建一个钩子,它允许组件订阅全局状态更改的一部分。例如,想象我的状态是这样的
{
products: []
userForm: {
name: 'John Smith',
dateOfBirth: '07/10/1991'
}
}
只有在 dateOfBirth 字段发生更改时,控制 userForm 中的字段的组件dateOfBirth
才应重新呈现。
假设我有一些使用 React 上下文创建的全局状态。这是我订阅该组件关心的全局状态字段的尝试
function useField(field) {
const [globalState, setGlobalState] = useContext(GlobalState);
const value = globalState[field] || "initial";
const setValue = useCallback(
(value) => {
setGlobalState((state) => ({
...state,
[field]: value
}));
},
[setGlobalState, field]
);
return [value, setValue];
}
演示https://codesandbox.io/s/dawn-fog-ieqxs?file=/src/App.js:326-612
上面的代码会导致任何使用useField
钩子的组件重新渲染。
期望的行为是组件只应在该字段更改时重新呈现。
解决方案
它可以工作,但不能使用 Context API,因为目前 Context API 无法拯救无用的渲染。
换句话说:订阅上下文提供者的组件将始终在提供者值更改时呈现。
Context API 已知问题的示例:
const GlobalContext = React.createContext(null);
const InnerComponent = () => {
/* eslint-disable no-unused-vars */
const { uselessState } = useContext(GlobalContext);
console.log(`Inner rendered`);
return <></>;
};
const InnerMemo = React.memo(InnerComponent);
const InnerComponentUsingContext = () => {
const { counter, dispatch } = useContext(GlobalContext);
console.log(`Inner Using Context rendered`);
return (
<>
<div>{counter}</div>
<button onClick={() => dispatch()}>Dispatch</button>
</>
);
};
const InnerComponentUsingContextMemo = React.memo(InnerComponentUsingContext);
const App = () => {
const [counter, dispatch] = useReducer((p) => p + 1, 0);
const [uselessState] = useState(null);
return (
<GlobalContext.Provider value={{ counter, uselessState, dispatch }}>
<InnerMemo />
<InnerComponentUsingContextMemo />
</GlobalContext.Provider>
);
};
也就是说,使用每个现代状态管理解决方案都有一个救助功能,可以解决这个问题:
// Always renders
const [globalState, setGlobalState] = useContext(GlobalState);
const value = globalState[field] || "initial";
// Bailout, for example with redux
const value = useReducer(globalState => globalState[field], /* Can add bailout function here if necessary */);
推荐阅读
- python - 有没有办法比我的脚本更快地读取 GB 大小的文本文件?
- sql - 使用两种形式将第一个表的主键的值获取到第二个表的外键中
- laravel - 使用 tymon/jwt-auth 函数代替 laravel 默认值
- angular - Angular 8 中基于组件的部署
- android - 您的应用包含公开的 Google Cloud Platform (GCP) API 密钥
- android - “这个项目包含资源错误,所以aapt没有成功,会导致渲染失败,先修复资源问题。” 在安卓工作室
- android - Microsoft Appcenter 中基于分支的部署
- jenkins - 删除 Jenkins 构建历史时出错
- reactjs - 如何检索组件的道具以在 Jest/Enzyme 中进行测试?
- kotlin - 如何在片段中的单个 RecyclerView 中使用 when 条件设置多个 CardView?