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

IT技术 reactjs react-hooks closures use-state
2021-03-26 22:29:07

这里的代码和框: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 关闭时重置。

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

2个回答

您已经定义了Content内部EditUser组件,我们从来没有react的组分做,因为在这个situtaion,Content将是重新创建每次时间EditUser重新呈现(当然, EditUser 将被重新渲染几次/多次)。

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

这就是为什么它被多次挂载并因此状态值重置为初始值的原因。

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

有道理,谢谢 那么当 React 去 render 时<Content />,是不是会发生这样的事情:“我必须渲染一个 Content 组件。Content 函数与我上次渲染这个 Component 不同,所以我必须卸载之前的 Content 组件并从头开始重新创建一个”?
2021-06-02 22:29:07
确切地!没错。当它呈现一个组件时,它会从头开始设置新状态,并且会丢失所有以前的状态值。我还没有尝试过,但我想,我们可以在组件中保留一个组件定义并使用useCallback钩子来防止它被重新创建,但它会以复杂的方式做一件简单的事情,所以不推荐。
2021-06-02 22:29:07

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

你能解释一下为什么吗?我有一种感觉,它与创建一个新的闭包(每次都会创建一个具有新状态的新组件)有关,但我对细节真的很模糊。我想快速了解一下在这种失败情况下会发生什么,以及在“正确”的情况下会发生什么。
2021-06-15 22:29:07
是的。它不应在组件内定义。
2021-06-17 22:29:07