[包括useContext钩子使子代的useState重置为初始值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[包括useContext钩子使子代的useState重置为初始值相关的知识,希望对你有一定的参考价值。

我已经为这个问题苦苦挣扎了几天,所以任何帮助将不胜感激。我们拥有一个全局数据上下文,该上下文包含在层次结构中的一些组件中。我已经复制了以下基本示例中遇到的问题。

您也可以run it on CodeSandbox

问题是,每次重新渲染childValue组件中的Content都会重置为其初始useState值。但这是[[only的情况,当useData上下文包含在Routes组件的链中。删除useData行(并硬编码isAuthenticated)可以解决此问题。但是,这不是可接受的解决方案,因为我们需要能够将某些值保留在全局范围内,并在任何地方包括它们。

我曾尝试将React.memo(...)中的内容包装无济于事。我在这里想念什么?

import React, { useState, useContext, useEffect } from "react"; import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import { render } from "react-dom"; // Routes const Routes = () => { // We see desired behavior if useData() is removed here. // i.e. Initial Value does NOT get reset in Content const { isAuthenticated } = useData(); // const isAuthenticated = true // uncomment this after removing the above line const RouteComponent = isAuthenticated ? PrivateRoute : Route; return ( <Switch> <RouteComponent path="/" render={props => <Content {...props} />} /> </Switch> ); }; const PrivateRoute = ({ render: Render, path, ...rest }) => ( <Route path={path} render={props => <Render isPrivate={true} {...props} />} {...rest} /> ); // Data Context export const DataContext = React.createContext(); export const useData = () => useContext(DataContext); export const DataProvider = ({ children }) => { const [isAuthenticated, setIsAuthenticated] = useState(false); const [contextValue, setContextValue] = useState(false); useEffect(() => { setIsAuthenticated(true); }, []); const doSomethingInContext = () => { setTimeout(() => setContextValue(!contextValue), 1000); }; return ( <DataContext.Provider value={{ isAuthenticated, doSomethingInContext, contextValue }} > {children} </DataContext.Provider> ); }; // Page Content const Content = props => { const { contextValue, doSomethingInContext } = useData(); const [childValue, setChildValue] = useState("Initial Value"); useEffect(() => { if (childValue === "Value set on Click") { doSomethingInContext(); setChildValue("Value set in useEffect"); } }, [childValue]); return ( <div> <div style={{ fontFamily: "monospace" }}>contextValue:</div> <div>{contextValue.toString()}</div> <br /> <div style={{ fontFamily: "monospace" }}>childValue:</div> <div>{childValue}</div> <br /> <button onClick={() => setChildValue("Value set on Click")}> Set Child Value </button> </div> ); }; const App = () => { return ( <DataProvider> <Router> <Routes /> </Router> </DataProvider> ); }; render(<App />, document.getElementById("root"));

答案
我认为问题是这样的:当您调用doSomethingInContext时,它将触发setContextValue(在超时后)。运行该命令时,它将更新Provider的数据,这会导致Routes重建(因为它是使用者)。重建Routes会更改render功能,导致所有下面的东西都被丢弃并重建。尝试useCallback:在Routes中,添加以下内容:

// In the body... const render = useCallback( props => <Content {...props} />, [] ); // In the RouteComponent <RouteComponent path="/" render={render} />

那样,功能不会改变,并且子项应保留在重建中。

以上是关于[包括useContext钩子使子代的useState重置为初始值的主要内容,如果未能解决你的问题,请参考以下文章

如何将 React 钩子(useContext、useEffect)与 Apollo 反应钩子(useQuery)结合起来

如何使用自定义钩子和 useContext 仅渲染相关组件

使用新的 React 钩子 useContext 的正确方法是啥?

在使用数据库数据更新上下文后,React 'useContext' 钩子不会重新渲染

使用 React 钩子 useContext 避免不必要的重新渲染

你如何在 React 中调用 useContext 而不破坏钩子规则?