从回调中调用 setState 会在一半时间内更新 React Context 中的状态

Calling setState from callback updates the state in React Context half the time

这是 CodeSandbox:https://codesandbox.io/s/competent-dream-8lcrt

我已经使用 React 上下文实现了一个模态,它公开了 1) 打开状态 2) 模态配置 3) 打开和关闭方法

// ...

const onOpenDialog = (mode, config) => {
  setDialogMode(mode)
  if (config) {
    setDialogConfig(config);
  }
}

const onCloseDialog = () => {
  setDialogMode("");
  if (Object.keys(dialogConfig).length > 0) {
    setDialogConfig({})
  }
}

// ...

return (
  <Provider value={{ dialogMode, dialogConfig, onOpenDialog, onCloseDialog }}>
    {children}
  </Provider>
)

在主要组件中,我有一个调用 onOpenDialog 方法并在其配置对象中传递 onSubmitonClose 回调的 onClick 处理程序(此 onCloseDialog 回调是问题)

const { onOpenDialog, onCloseDialog } = useDialog()

// ...

const onClick = () => {
  onOpenDialog("add", {
    data: null,
    onSubmit: (data) => {
      console.log("Form Data: ", data)
    },
    onCancel: onCloseDialog
  })
}

最后,我有一个 FormInModal 组件,它在点击提交和关闭时调用 dialogConfig 对象中传递的两个回调。

const onSubmit = (data) => {
  dialogConfig.onSubmit({
    username: data.username,
    password: data.password
  })
}

const onCancel = () => {
  dialogConfig.onCancel()
}

重现步骤:

  1. 点击Open
  2. 您应该在控制台中看到 1) 对话框打开 2) 传递给对话框配置的配置输入有数据、onSubmit 和 onCancel,配置输入已设置在 dialogConfig 状态
  3. 单击取消,您应该会看到 1) 对话框关闭 2) dialogConfig 状态为空 3) dialogConfig 状态未重置

公平地说 3. 有一半的时间有效。这很奇怪,因为 dialogConfig 状态总是在对话框打开时更新。你可以在 React Dev 工具上看到状态更新只有一半的时间

这是 React 中尝试从函数闭包访问状态时的一个已知问题。旧状态在闭包创建时被捕获并由函数返回,即使状态已经更新。

解决该问题的一种方法是通过以下方式访问更新后的状态:

setState(previousState => newState) 而不是 setState(newState)

反应社区的回答:Function that read an outdated sate