【问题标题】:React useContext losing state反应 useContext 失去状态
【发布时间】:2021-03-16 21:50:12
【问题描述】:

过去几天我一直在尝试解决一个问题:

我正在为表单字段使用上下文提供程序,并且无论出于何种原因,当我使用备忘录时,字段都会相互覆盖。

提供者:

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

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

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

加法:

我已经尝试过为此使用内部 reducer 并传递一个 dispatch 函数。只要我不尝试在提供者之外管理状态,一切正常。

【问题讨论】:

    标签: javascript reactjs react-state-management use-context


    【解决方案1】:

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

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

    假设field1 的值发生了变化。它调用onChange 处理程序,该处理程序将...formValues(安装组件时存在的值)与field1 的新值合并。

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

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

    对此的简单解决方案是使用setState 的回调版本,它始终使用状态的当前值:

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

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

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

    【讨论】:

    • 非常感谢您的意见/帮助!这行得通!我在过去的两个晚上几乎没有睡觉!我实现 React.memo 的原因实际上是“因为”我可以看到渲染/性能问题。我的表单有 30 多个字段,分布在不同的选项卡上,按键盘会产生明显的延迟
    • 很公平 - 如果你能看到差异,那么它可能是值得的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-31
    • 2022-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多