为什么 usePrevious 在第一次渲染时返回 undefined - 反应钩子?

时间:2021-06-18 15:02:56

标签: reactjs api react-hooks use-ref

我想将前一个值存储在一个变量中并在函数中使用它。假设 当前值是 9之前的值应该是 8就像少一个) .问题是 console.log(prevServings) 在第一次渲染时返回 undefined 并在第二次渲染时显示前一个值,但是当前值和前一个值之间的差异是 2 而不是 1.我的理解是当前值在第一次渲染时不可用,因此前一个值未定义,但我不知道如何修复它。任何帮助,将不胜感激。提前致谢。

const Child = ({originalData}) =>{
  //clone original data object with useState
  const [copyData, setCopyDta] = useState({});
  //clone the copied data 
  let duplicate = {...copyRecipe};
  
  
  //store previous servings value into a variable
  const usePrevious = (servings) => {
    const ref = useRef();
    useEffect(() => {
      ref.current = servings;
    }, [servings]);

    return ref.current;
  };
  const prevServings = usePrevious(duplicate.servings);
  
  //increase the number of servings on click
  const incrementHandle = () => {
    duplicate.servings = `${parseInt(duplicate.servings) + 1}`;
    //this return undefined on the first render
    console.log(prevServings);
    setCopyRecipe(duplicate);
  }

  return(
    <p>{copyData.servings}</p>
    <Increment onClick={incrementHandle}/>
  )
}

2 个答案:

答案 0 :(得分:0)

它返回 undefined 因为 useEffect() 至少在第一次渲染之后才会触发。你可能想要这样做:

const usePrevious = (servings) => {
  const ref = useRef(servings);
  useEffect(() => {
    ref.current = servings;
  }, [servings])
  return ref.current;
}

不过,这确实让人难以推理。我可能会建议改用减速器或常规状态。如果您不希望组件对该特定值的更改做出“反应”,则 ref 很有用,但是每次 ref 在这里更改时,您无论如何都会触发状态更新。

答案 1 :(得分:0)

问题

糟糕,不是完全重复。这里的问题是,由于您已经声明了 usePrevious inside 组件,它会在每个渲染周期重新创建,从而有效地否定了任何缓存工作。

解决方案

usePrevious 钩子移出组件主体,使其成为一个稳定的参考。您可能还想删除 useEffect 的依赖项,以便在每个渲染周期缓存该值。

//store previous servings value into a variable
const usePrevious = (value) => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

const Child = ({originalData}) =>{
  //clone original data object with useState
  const [copyData, setCopyDta] = useState({});
  //clone the copied data 
  let duplicate = { ...copyData };
  
  const prevServings = usePrevious(duplicate.servings);
  
  //increase the number of servings on click
  const incrementHandle = () => {
    duplicate.servings = `${parseInt(duplicate.servings) + 1}`;
    setCopyDta(duplicate);
  }

  return(
    <p>{copyData.servings}</p>
    <Increment onClick={incrementHandle}/>
  )
}

仅供参考

仅供参考,let duplicate = { ...copyRecipe }; 只是一个浅拷贝而不是对象的克隆,所有嵌套的属性仍将引用回原始对象中的对象。也许这就是你所需要的,但只是想指出这不是对象的真正克隆。

QnA

<块引用>

问题是 console.log(prevServings) 在 第一次渲染并在第二次渲染时显示前一个值

我认为这应该是预期的行为,因为在初始渲染时没有先前的渲染来缓存值。

<块引用>

但是当前值和先前值之间的差为 2 共 1 个。

关于“off-by-2”问题,据我所知,每次渲染都会缓存 duplicate 的未更新浅表副本,当点击 incrementHandle 时,您会记录 {{1} } value 然后 将触发重新渲染的更新排入队列。您记录的值和状态更新结果(即 prevServings)相隔两个渲染周期。如果在渲染周期的同一点比较两个值,您会发现它们相差 1。

<p>{copyData.servings}</p>

日志输出:

<块引用>
useEffect(() => {
  console.log({ prevServings, current: copyData.servings })
})