如果组件在另一个组件中定义,则在渲染之间丢失状态

Losing state between renders if component is defined in another component

codesandbox 在这里:https://codesandbox.io/s/restless-haze-v01wv?file=/src/App.js

我有一个用户组件(简化后)看起来像这样:

const Users = () => {
  const [toastOpen, setToastOpen] = useState(false)

  // functions to handle toast closing
  return (
   <EditUser />
   <Toast />
  )
}

const EditUser = () => {
  [user, setUser] = useState(null)
  useEffect(() => {
    const fetchedUser = await fetchUser()
    setUser(fetchedUser)
  }, [])

  // this approach results in UserForm's username resetting when the toast closes
  const Content = () => {
    if (user) return <UserForm user={user} />
    else return <div>Loading...</div>
  }
  return <Content />

  // if I do this instead, everything's fine
  return (
    <div>
    {
      user ? <UserForm user={user} /> : <div>Loading...</div>
    }
    </div>
  )
}

const UserForm = ({ user }) => {
  const [username, setUsername] = useState(user.name)

  return <input value={username}, onChange={e => setUsername(e.target.value)} />
}

在 Toast 仍然打开的情况下查看用户窗体页面时,用户窗体状态会在 Toast 关闭时重置。

我发现问题出在 EditUser 内部定义的 Content 组件,但我不太清楚为什么这是个问题。我喜欢在这里演练一下 React 引擎盖下发生的事情,以及在“快乐之路”中发生的事情

您已经在 EditUser 组件中定义了 Content,而我们在 React Components 中从未这样做过,因为在这种情况下,Content 将被 重新创建 每次 EditUser 重新渲染 。 (当然,EditUser 将被重新渲染 few/many 次)。

因此,重新创建的 Content 组件意味着旧的 Content 将被销毁(卸载)并安装新的 Content

这就是为什么它被多次安装,因此 重置 状态值 到初始值。

因此,解决方案是在外部定义它(内容)- 而不是在任何其他 React 组件内部

罪魁祸首是 EditUser 的 Content 函数,可以预见,每次调用它时 returns 都是一个全新的实例。