首页 > 解决方案 > 反应 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 打印美化器来跟踪每个字段的状态,并且当我只编辑一个字段时,它会一直运行到每个字段。但是,一旦我开始编辑另一个字段,我编辑的第一个字段就会回到它的原始状态和/或它过去的状态之间的其他一些奇怪的状态。

如果我不使用备忘录,它可以工作,但这不是解决方案,因为我将处理很多字段,这会导致大量重新渲染。

有人知道这里发生了什么吗?

添加:

我已经尝试为此使用内部减速器并传递调度函数。只要我不尝试在提供者之外管理状态,一切都会正常工作。

标签: javascriptreactjsreact-state-managementuse-context

解决方案


我很确定问题是您正在formValues从这里记住原始值:

onChange={({ name, value }) => setFormValues({...formValues, [name]: value }))

Sayfield1的值发生了变化。它调用onChange处理程序,该处理程序将...formValues- 组件安装时存在的值 - 与field1.

现在React.memofor中的相等函数field1返回 false,因为值不同。该特定字段重新呈现以接收其新值,以及...formValues. 但是,其他字段尚未重新渲染。对他们来说,...formValues仍然意味着他们上次重新渲染时存在的状态值,也就是组件安装时的状态。

如果您现在更改 的值field2,它会将状态设置为将原始状态与 的新值合并的结果field2。因此field1被重置,因为它的值现在又变回了原始值。

一个简单的解决方案是使用 的回调版本setState,它总是使用状态的当前值:

onChange={({ name, value }) => setFormValues(fv => {...fv, [name]: value }))

但是,我很想不这样做,而是完全摆脱记忆。这是因为您的相等函数实际上并不能准确地反映提供给组件的道具更改的方式。我相信这里的性能提升也可以忽略不计,因为组件非常小,并且本身不会渲染任何额外的组件。

假设没有与值更改相关联的动画,执行起来非常便宜,并且不适合记忆化,这也逃脱了内置的 React 优化。在实施之前,您应该仔细考虑确定您是否真的需要它。


推荐阅读