javascript - 反应 useContext 失去状态
问题描述
过去几天我一直在尝试解决一个问题:
我正在为表单字段使用上下文提供程序,并且无论出于何种原因,当我使用备忘录时,字段都会相互覆盖。
提供者:
export const Context = React.createContext();
function Form_Provider({ values, onChange, children }) {
return (
<Context.Provider value={{ values, onChange }}>
{children}
</Context.Provider>
)
});
export default Form_Provider
场地:
function Field({ label, name }) {
const { values, onChange } = useContext(Context);
return (
<Memorable
label={label}
onChange={({ target: t }) => onChange({ name, value: t.value })}
value={values[name]}
/>
);
}
const Memorable = React.memo(props => {
return (
<Form.Item label={props.label}>
<Input
value={props.value}
onChange={props.onChange}
/>
</Form.Item>
</>
)
}, ({ value: newValue}, { value: oldValue }) => newValue == oldValue)
形式
const [formValues, setFormValues] = useState({ field1: 'Foo', field2: 'Bar' });
<Form.Provider
values={formValues}
onChange={({ name, value }) => setFormValues({...formValues, [name]: value }))
>
<Form.Field name='field1' label="Field 1" />
<Form.Field name='field2' label="Field 2" />
</Form.Provider>
(尽量简化)
在我的实际代码中,我添加了一个 json 打印美化器来跟踪每个字段的状态,并且当我只编辑一个字段时,它会一直运行到每个字段。但是,一旦我开始编辑另一个字段,我编辑的第一个字段就会回到它的原始状态和/或它过去的状态之间的其他一些奇怪的状态。
如果我不使用备忘录,它可以工作,但这不是解决方案,因为我将处理很多字段,这会导致大量重新渲染。
有人知道这里发生了什么吗?
添加:
我已经尝试为此使用内部减速器并传递调度函数。只要我不尝试在提供者之外管理状态,一切都会正常工作。
解决方案
我很确定问题是您正在formValues
从这里记住原始值:
onChange={({ name, value }) => setFormValues({...formValues, [name]: value }))
Sayfield1
的值发生了变化。它调用onChange
处理程序,该处理程序将...formValues
- 组件安装时存在的值 - 与field1
.
现在React.memo
for中的相等函数field1
返回 false,因为值不同。该特定字段重新呈现以接收其新值,以及...formValues
. 但是,其他字段尚未重新渲染。对他们来说,...formValues
仍然意味着他们上次重新渲染时存在的状态值,也就是组件安装时的状态。
如果您现在更改 的值field2
,它会将状态设置为将原始状态与 的新值合并的结果field2
。因此field1
被重置,因为它的值现在又变回了原始值。
一个简单的解决方案是使用 的回调版本setState
,它总是使用状态的当前值:
onChange={({ name, value }) => setFormValues(fv => {...fv, [name]: value }))
但是,我很想不这样做,而是完全摆脱记忆。这是因为您的相等函数实际上并不能准确地反映提供给组件的道具更改的方式。我相信这里的性能提升也可以忽略不计,因为组件非常小,并且本身不会渲染任何额外的组件。
假设没有与值更改相关联的动画,执行起来非常便宜,并且不适合记忆化,这也逃脱了内置的 React 优化。在实施之前,您应该仔细考虑确定您是否真的需要它。
推荐阅读
- postman - 在基于 SAML/shibboleth 的网站上使用 Postman 进行身份验证,无需使用浏览器
- c - 为什么这显示“无效的类型转换”?
- winapi - 在 64 位和 32 位 (WOW64) 应用程序之间使用 WM_COPYDATA 是否安全?
- git - Kde SVN2Git。错误:存储库“my_repo”中的“branch_1”从分支“branch_2”分支,但后者不存在。无法继续
- sharepoint - 有没有办法将筛选器发送到 Sharepoint 列表并在 Web 部件中查看结果?
- html - Firefox 无法正确处理 svg 图像,但 Chrome 可以
- excel-formula - 我需要根据两个不同单元格中的信息在一个单元格中返回两个响应之一(留空或添加文本)
- python-3.x - MULTI 选项中的输入不会在 dash python 中产生预期的输出
- c# - 具有相等 DateTimeOffset 日期的单元测试返回错误
- java - Java:使用内置函数对数组进行排序时出错