Reactjs 反应:嵌套函数中未更新useContext值

Reactjs 反应:嵌套函数中未更新useContext值,reactjs,nested-function,use-context,Reactjs,Nested Function,Use Context,我有一个简单的上下文,它设置了一些从后端获得的值,伪代码: export const FooContext = createContext(); export function Foo(props) { const [value, setValue] = useState(null); useEffect(() => { axios.get('/api/get-value').then((res) => { const da

我有一个简单的上下文,它设置了一些从后端获得的值,伪代码:

export const FooContext = createContext();

export function Foo(props) {
    const [value, setValue] = useState(null);

    useEffect(() => {
        axios.get('/api/get-value').then((res) => {
            const data = res.data;
            setValue(data);
        });
    }, []);

    return (
        <FooContext.Provider value={[value]}>
            {props.children}
        </FooContext.Provider>
    );
}

function App() {
    return (
        <div className="App">
            <Foo>
                <SomeView />
            </Foo>
        </div>
    );
}

function SomeView() {
    const [value] = useContext(FooContext);
    
    console.log('1. value =', value);
    
    const myFunction = () => {
        console.log('2. value = ', value);
    }
    
    return (<div>SomeView</div>)

因此,基本上出于某种原因,该值在嵌套函数中保持为null,尽管已更新为
'x'

我认为提供程序值中存在错误的部分,请执行以下操作:

...
<FooContext.Provider value={value}> // <-- 'value' is simply a value
    {props.children}
</FooContext.Provider>
...
但这是错误的,因为您将提供程序值设置为简单值而不是元组

解决方案要使您的分解工作正常,您应该如下设置提供程序

...
<FooContext.Provider value={[value]}> // <---- USE TUPLE NOTATION
    {props.children}
</FooContext.Provider>
...
const {value, setValue} = useContext(FooContext);
。。。

//首先,如果没有值,则需要为上下文提供一些默认值,然后将默认值设置为null

export const FooContext = createContext(null);
通常,在提供者组件中传递值有两种方式。您可以将
对象
元组
传递给提供者组件中的
道具

我将通过在Provider组件中传递一个
对象来为您提供一个示例@dna给出了一个
元组的例子

<FooContext.Provider value={{value,setValue}}>
     {props.children}
</FooContext.Provider>

如果如下图所示正确调用了嵌套的
myFunction()
,那么该值也将是
x
,而不是null

function SomeView() {
    const [value] = useContext(FooContext);
    
    console.log('1. value =', value);
    
    const myFunction = () => {
        console.log('2. value = ', value);
    }
    SomeView.myFunction = myFunction; //updated line
    return (<div>SomeView</div>)
}
现在,问题是它为什么返回单个字符值而不是状态值

在Javascript中,字符串是一个字符数组。 例如

在您的情况下,状态值可能是一个字符串。因此,当您解构字符串时,您将获得字符串的第一个字符

如果我给你举个例子,你会理解得更多

const string=“subrato”;
常量[str]=字符串;

console.log(str)值状态在函数中返回值并在嵌套函数中返回null的原因是因为这些原因

  • 第一个原因是您正在传递一个arrow函数,这意味着它将只返回onClick函数上的值

    const myFunction = () => {
         console.log('2. value = ', value);
     }
    
      function myFunction() {
        console.log("2. value = ", value);
      }
    
  • 第二个原因是您没有调用您创建的嵌套函数
    myFunction

  • 而不是这样做,它将起作用:

    • SomeView()中调用函数

    • 更新你的函数

      const myFunction = () => {
           console.log('2. value = ', value);
       }
      
        function myFunction() {
          console.log("2. value = ", value);
        }
      
    • 或者,如果希望它与旧代码一起运行,请传递一个onClick侦听器,它将启动myFunction(),并将console.log记录该值

       const myFunction = () => {
          console.log("2. value = ", value);
        };
      
       return <button onClick={myFunction}>Click me</button>;
      
      constmyfunction=()=>{
      console.log(“2.value=,value”);
      };
      返回点击我;
      
    解释 这是如此经典。我不知道闭包在哪里过时,因为您没有向我们展示如何使用
    myFunction
    ,但我确信这就是原因

    你知道,在JS中,每当你创建一个函数时,它将在它的闭包中捕获周围的范围,在它的创建点问题中的代码>值

    是这些状态之一

    但是调用
    myFunction
    可能会在以后某个时候发生,因为您可以传递
    myFunction
    。比方说,您将它传递给
    setTimeout(myFunction,1000)
    某个地方

    现在,在1000毫秒超时之前,假设
    组件已经在
    axios.get
    中重新呈现,并且
    值更新为
    'x'

    此时创建了
    myFunction
    的新版本,在该版本的结尾处捕获了新值
    value='x'
    。但是
    setTimeout
    传递的是
    myFunction
    的较旧版本,它捕获
    value=null
    。1000毫秒后,调用
    myFunction
    ,并打印
    2。value=null
    。事情就是这样


    解决方案 与所有其他编程问题一样,正确处理过时闭包问题的最佳方法是充分了解根本原因。一旦意识到了这一点,就要谨慎地编写代码,更改设计模式或其他任何东西。首先要避免这个问题,不要让它发生

    这里讨论了这个问题,请参阅github上的。在线程中,建议了多种模式和良好实践

    我不知道你具体案例的细节,所以我不知道什么是解决你问题的最佳方式。但一个非常简单的策略是使用对象属性而不是变量

    function SomeView() {
        const [value] = useContext(FooContext);
    
        const ref = useRef({}).current;
        ref.value = value;
        
        console.log('1. value =', value);
        
        const myFunction = () => {
            console.log('2. value = ', ref.value);
        }
    
        return (<div>SomeView</div>)
    }
    
    函数SomeView(){
    const[value]=useContext(FooContext);
    const ref=useRef({}).current;
    参考值=价值;
    console.log('1.value=',value);
    常量myFunction=()=>{
    console.log('2.value=',ref.value);
    }
    返回(SomeView)
    }
    
    其思想是依赖于对象的稳定引用

    ref=useRef({}).current
    为同一对象创建一个稳定的引用
    ref
    ,该引用在重新渲染时不会改变。然后将其放在
    myFunction
    的闭包中。它就像一个门户,“传送”状态更新越过闭包的边界


    现在,即使陈旧的闭包问题仍然存在,有时您仍然可以调用过时版本的
    myFunction
    ,这是无害的!因为旧的
    ref
    与新的
    ref
    相同,并且它的属性
    ref.value
    保证是最新的,因为在重新渲染时总是重新分配它
    ref.value=value

    在哪里调用
    myFunction
    ?它似乎在代码中,myFunction并没有被调用,对我来说就像一个过时的闭包,尽管我不能说,因为你的伪代码函数并没有在任何地方被调用,每次我点击一个按钮(不是在代码中)myFunction都会被调用。按钮是组件的一部分。添加了我的更新答案,其中说明了如何在父函数之外调用嵌套函数?@Bob SacamanoI很抱歉误导您。我将provider中的值设置为元组。我没有正确地编写伪代码。在实际情况中,它是一个元组。这个答案非常有用。是的,这确实是一个陈旧的问题:)
     myFunction();
    
      function myFunction() {
        console.log("2. value = ", value);
      }
    
     const myFunction = () => {
        console.log("2. value = ", value);
      };
    
     return <button onClick={myFunction}>Click me</button>;
    
    function SomeView() {
        const [value] = useContext(FooContext);
    
        const ref = useRef({}).current;
        ref.value = value;
        
        console.log('1. value =', value);
        
        const myFunction = () => {
            console.log('2. value = ', ref.value);
        }
    
        return (<div>SomeView</div>)
    }