Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript React hooks:从回调中访问最新状态_Javascript_Reactjs_React Hooks - Fatal编程技术网

Javascript React hooks:从回调中访问最新状态

Javascript React hooks:从回调中访问最新状态,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,编辑(2020年6月22日):由于这个问题重新引起了人们的兴趣,我意识到可能存在一些困惑。因此,我想强调:问题中的示例是一个玩具示例。这不是问题的反映。引发这个问题的问题是使用第三方库(控制有限),该库将回调作为函数的参数。为回调提供最新状态的正确方法是什么。在react类中,这可以通过使用this来完成。在React钩子中,由于状态封装在函数React.useState()中的方式,如果回调通过React.useState()获取状态,它将过时(设置回调时的值)。但是如果它设置了状态,它将通过

编辑(2020年6月22日):由于这个问题重新引起了人们的兴趣,我意识到可能存在一些困惑。因此,我想强调:问题中的示例是一个玩具示例。这不是问题的反映。引发这个问题的问题是使用第三方库(控制有限),该库将回调作为函数的参数。为回调提供最新状态的正确方法是什么。在react类中,这可以通过使用
this
来完成。在React钩子中,由于状态封装在函数
React.useState()
中的方式,如果回调通过
React.useState()
获取状态,它将过时(设置回调时的值)。但是如果它设置了状态,它将通过传递的参数访问最新的状态。这意味着,通过将状态设置为原来的状态,我们可以通过React钩子在这样的回调中获得最新的状态。这是可行的,但与直觉相反

--原问题继续如下--

我正在使用React钩子并试图从回调中读取状态。每次回调访问它时,它都会返回其默认值

使用以下代码。无论我点击多少次,控制台都将继续打印
计数为:0

function Card(title) {
  const [count, setCount] = React.useState(0)
  const [callbackSetup, setCallbackSetup] = React.useState(false)
  
  function setupConsoleCallback(callback) {
    console.log("Setting up callback")
    setInterval(callback, 3000)
  }

  function clickHandler() {
    setCount(count+1);
    if (!callbackSetup) {
      setupConsoleCallback(() => {console.log(`Count is: ${count}`)})
      setCallbackSetup(true)
    }
  }
  
  
  return (<div>
      Active count {count} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>);
  
}

const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component' />, el);

你可以找到这个代码

这种方法也没有奏效。 编辑:实际上,第二种方法确实有效。我刚才回电话时打错了。这是正确的做法。我需要调用setState来访问上一个状态。即使我无意设定国家

我觉得我对React类采取了类似的方法,但是。为了代码的一致性,我需要坚持使用React效果


如何从回调中访问最新状态信息?

使用
useffect
而不是尝试访问回调中的最新状态。使用从
setState
返回的函数设置您的状态不会立即更新您的值。状态更新是批处理和更新的

如果您想到
useffect()
类似于
setState
的第二个参数(来自基于类的组件),它可能会有所帮助

如果要使用最新状态执行操作,请使用
useffect()
,当状态更改时,将点击该选项:

const{
useState,
使用效果
}=反应;
函数App(){
const[count,setCount]=useState(0);
常量减量=()=>setCount(count-1);
常量增量=()=>setCount(count+1);
useffect(()=>{
console.log(“useffect”,count);
},[计数];
console.log(“呈现”,计数);
报税表(
{count}

- + ); } const rootElement=document.getElementById(“根”); render(,rootElement)
我知道的唯一方法是调用setState(current_value=>…)函数并在逻辑中使用current_值。一定要把它还给我。例:

const myPollingFunction = () => {
    setInterval(() => {
        setState(latest_value => {
            // do something with latest_value
            return latest_value;    
        }
    }, 1000);
};
对于您的场景(您无法继续创建新回调并将其传递给第三方库),您可以使用
useRef
以保持具有当前状态的可变对象。像这样:

function Card(title) {
  const [count, setCount] = React.useState(0)
  const [callbackSetup, setCallbackSetup] = React.useState(false)
  const stateRef = useRef();

  // make stateRef always have the current count
  // your "fixed" callbacks can refer to this object whenever
  // they need the current value.  Note: the callbacks will not
  // be reactive - they will not re-run the instant state changes,
  // but they *will* see the current value whenever they do run
  stateRef.current = count;

  function setupConsoleCallback(callback) {
    console.log("Setting up callback")
    setInterval(callback, 3000)
  }

  function clickHandler() {
    setCount(count+1);
    if (!callbackSetup) {
      setupConsoleCallback(() => {console.log(`Count is: ${stateRef.current}`)})
      setCallbackSetup(true)
    }
  }


  return (<div>
      Active count {count} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>);

}
功能卡(标题){
const[count,setCount]=React.useState(0)
const[callbackSetup,setCallbackSetup]=React.useState(false)
const stateRef=useRef();
//使stateRef始终具有当前计数
//无论何时,您的“固定”回调都可以引用此对象
//它们需要当前值。注意:回调不会
//被动-他们不会重新运行即时状态更改,
//但无论何时运行,它们都会看到当前值
stateRef.current=计数;
函数设置控制台回调(回调){
log(“设置回调”)
设置间隔(回调,3000)
}
函数clickHandler(){
设置计数(计数+1);
如果(!callbackSetup){
setupConsoleCallback(()=>{console.log(`Count为:${stateRef.current}`)})
setCallbackSetup(true)
}
}
返回(
活动计数{count}
增量 ); }

回调可以引用可变对象来“读取”当前状态。它将在闭包中捕获可变对象,每次渲染时,可变对象都将使用当前状态值进行更新。

我遇到了一个类似的错误,试图执行与示例中完全相同的操作-在引用React组件中的
道具或
状态的回调上使用
setInterval

希望我能从一个稍微不同的方向来解决这个问题,从而补充这里已经给出的好答案——认识到这甚至不是一个React问题,而是一个普通的老Javascript问题

我认为这里吸引人的是React-hooks模型,其中状态变量,毕竟只是一个局部变量,可以被视为在React组件的上下文中是有状态的。您可以确定,在运行时,变量的值将始终是React在特定状态下保持的值

然而,一旦您脱离了React组件上下文(例如,在
setInterval
内的函数中使用变量),抽象就中断了,您回到了这样一个事实:状态变量实际上只是一个持有值的局部变量

抽象允许您编写代码,就好像运行时的值总是反映状态一样。在React的上下文中,情况就是这样,因为只要设置状态,整个函数就会再次运行,并且变量的值由React设置为更新后的状态值。然而,在回调内部,没有发生这样的事情——该变量不会神奇地更新以反映underlyn
const myPollingFunction = () => {
    setInterval(() => {
        setState(latest_value => {
            // do something with latest_value
            return latest_value;    
        }
    }, 1000);
};
function Card(title) {
  const [count, setCount] = React.useState(0)
  const [callbackSetup, setCallbackSetup] = React.useState(false)
  const stateRef = useRef();

  // make stateRef always have the current count
  // your "fixed" callbacks can refer to this object whenever
  // they need the current value.  Note: the callbacks will not
  // be reactive - they will not re-run the instant state changes,
  // but they *will* see the current value whenever they do run
  stateRef.current = count;

  function setupConsoleCallback(callback) {
    console.log("Setting up callback")
    setInterval(callback, 3000)
  }

  function clickHandler() {
    setCount(count+1);
    if (!callbackSetup) {
      setupConsoleCallback(() => {console.log(`Count is: ${stateRef.current}`)})
      setCallbackSetup(true)
    }
  }


  return (<div>
      Active count {count} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>);

}
class Count {
  constructor (val) { this.val = val }
  get () { return this.val }
  
  update (val) {
    this.val += val
    return this
  }
}

function Card(title) {
  const [count, setCount] = React.useState(new Count(0))
  const [callbackSetup, setCallbackSetup] = React.useState(false)
  
  function setupConsoleCallback(callback) {
    console.log("Setting up callback")
    setInterval(callback, 3000)
  }

  function clickHandler() {
    setCount(count.update(1));
    if (!callbackSetup) {
      setupConsoleCallback(() => {console.log(`Count is: ${count.get()}`)})
      setCallbackSetup(true)
    }
  }
  
  
  return (
    <div>
      Active count {count.get()} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>
  )
}

const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component' />, el);
import React from 'react';

const INTERVAL_FOR_TIMER_MS = 3000;

export function Card({ title }) {
  const [count, setCount] = React.useState(0)

  React.useEffect(
    () => {
      const intervalId = setInterval(
        () => console.log(`Count is ${count}`),
        INTERVAL_FOR_TIMER_MS,
      );
      return () => clearInterval(intervalId);
    },
    // you only want to restart the interval when count changes
    [count],
  );

  function clickHandler() {
    // I would also get in the habit of setting this way, which is safe
    // if the increment is called multiple times in the same callback
    setCount(num => num + 1);
  }

  return (
    <div>
      Active count {count} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>
  );
}
import React from 'react';

const INTERVAL_FOR_TIMER_MS = 3000;

const useInterval = (func, period, deps) => {
  const [nextPopTime, setNextPopTime] = React.useState(
    Date.now() + period,
  );
  React.useEffect(() => {
    const timerId = setTimeout(
      () => {
        func();
        
        // setting nextPopTime will cause us to run the 
        // useEffect again and reschedule the timeout
        setNextPopTime(popTime => popTime + period);
      },
      Math.max(nextPopTime - Date.now(), 0),
    );
    return () => clearTimeout(timerId);
  }, [nextPopTime, ...deps]);
};

export function Card({ title }) {
  const [count, setCount] = React.useState(0);

  useInterval(
    () => console.log(`Count is ${count}`),
    INTERVAL_FOR_TIMER_MS,
    [count],
  );

  return (
    <div>
      Active count {count} <br/>
      <button onClick={() => setCount(num => num + 1)}>
        Increment
      </button>
    </div>
  );
}
import useState from 'react-usestateref'

const [count, setCount,counterRef] useState(0)

console.log(couterRef.current) // it will always has the latest state value
setCount(20);
console.log(counterRef.current)
function useExtendedState<T>(initialState: T) {
  const [state, setState] = React.useState<T>(initialState);
  const getLatestState = () => {
    return new Promise<T>((resolve, reject) => {
      setState((s) => {
        resolve(s);
        return s;
      });
    });
  };

  return [state, setState, getLatestState] as const;
}
const [counter, setCounter, getCounter] = useExtendedState(0);

...

getCounter().then((counter) => /* ... */)

// you can also use await in async callback
const counter = await getCounter();
onClick={() => { clickHandler(); }}
onClick={e => clickHandler()}
  // once when the component is mounted
  constructor(initialValue) {
    this.args = Object.freeze([initialValue, this.updater]);
  }

  // every time the Component is invoked
  update() {
    return this.args
  }

  // every time the setState is invoked
  updater(value) {
    this.args = Object.freeze([value, this.updater]);
    this.el.update()
  }

  const Component = () => {
    const [state, setState] = useState(initialValue)
  }
  function doSomething(v) {
    // I need to wait for 10 seconds
    // within this period of time
    // UI has refreshed multiple times
    // I'm in step 4)
    console.log(v)
  }

  // Invoke this function in step 1)
  doSomething(value)