我为什么会得到旧的useRef()值?

huangapple go评论73阅读模式
英文:

Why do I get old useRef() value?

问题

我创建了一个钩子,其目的是将樱桃添加到一个数组中:

然后我在按钮点击时调用它:

我不明白为什么在调用dispatchCherry()之后cherryRef.current会返回旧值。理论上,它应该返回增加后的值,因为myRef是一个对象。

我试图在调用dispatch()之后立刻获取增加后的值。

英文:

I created a hook, whose purpose is adding cherries to an array:

import { useReducer, useRef } from "react";

const useCherry = () => {
    const myRef = useRef(0);

    const [state, dispatch] = useReducer(
        (state, action) => {
            if (action.type === "add") {
                myRef.current += 1;
                return [...state, "🍒"];
            }

            return state;
        },
        []
    );

    return [state, dispatch, myRef];
};

export default useCherry;

Then I call it on button click:

<button
    type="button"
    onClick={() => {
        console.log(myRef.current); // Logs 0
        dispatch({ type: "add" });
        console.log(myRef.current);// Logs 0, expected 1
    }}
>
    Add more cherry
</button>

I don't understand why would cherryRef.current return an old value after calling dispatchCherry(). In theory it should return the incremented one, as myRef is an object.

I'm trying to get the incremented value right after calling the dispatch().

答案1

得分: 3

因为 dispatch() 运行并更新下一次渲染的状态,第二个 console.log() 在它之前运行,因此返回旧值。

要在更改前后分别存储和检索 useRef 值(在 state 之外),考虑导出一个自定义的 dispatch() 供另一个组件使用。

const myDispatchCherry = (action) => {
  if (action?.type === "add") myRef.current += 1;
  dispatch(action);
}

这还可以使原始的 dispatch() 保持纯净,并将附加任务作为可选的可定制部分添加。

在这里测试以下代码:stackblitz

import React, { useReducer, useRef } from "react";

const useCherry = () => {
    const myRef = useRef(0);
    const [state, dispatch] = useReducer(
        (state, action) => {
            if (action.type === "add") {
                return [...state, "🍒"];
            }
            return state;
        },
        []
    );

    const myDispatchCherry = (action) => {
        if (action?.type === "add") myRef.current += 1;
        dispatch(action);
    }

    return {state, dispatch, myRef, myDispatchCherry};
};

export default useCherry;

export default function App() {
  const {state, myRef, myDispatchCherry} = useCherry();
  return (
    <div>
      <p>{`Cherry: ${state}`}</p>
      <button
        type="button"
        onClick={() => {
            console.log(`myRef count before adding: ${myRef.current}`); // Logs 0
            myDispatchCherry({ type: "add" });
            console.log(`myRef count after adding: ${myRef.current}`); // Logs 1
        }}
      >
        Add more cherry
      </button>
    </div>
  );
}
英文:

Because dispatch() runs and updates the state for the next render, the second console.log() runs before it, and thus returns the old value.

To store and retrieve the useRef value before and after the change separately (outside of the state), consider to export a customized dispatch() for use in another component.

const myDispatchCherry = (action) =&gt; {
  if (action?.type === &quot;add&quot;) myRef.current += 1;
  dispatch(action);
}

This could also keep the original dispatch() pure to its function, and add the additional tasks as an optional and customizable part.

Test the follow code here: stackblitz

import React, { useReducer, useRef } from &quot;react&quot;;

const useCherry = () =&gt; {
    const myRef = useRef(0);
    const [state, dispatch] = useReducer(
        (state, action) =&gt; {
            if (action.type === &quot;add&quot;) {
                return [...state, &quot;&#127826;&quot;];
            }
            return state;
        },
        []
    );

const myDispatchCherry = (action) =&gt; {
  if (action?.type === &quot;add&quot;) myRef.current += 1;
  dispatch(action);
}
    return {state, dispatch, myRef, myDispatchCherry};
};

export default useCherry;

export default function App() {
  const {state, myRef, myDispatchCherry} = useCherry();
  return (
    &lt;div&gt;
      &lt;p&gt;{`Cherry: ${state}`}&lt;/p&gt;
      &lt;button
    type=&quot;button&quot;
    onClick={() =&gt; {
        console.log(`myRef count before adding: ${myRef.current}`); // Logs 0
        myDispatchCherry({ type: &quot;add&quot; });
        console.log(`myRef count after adding: ${myRef.current}`); // Logs 1
    }}
&gt;
    Add more cherry
&lt;/button&gt;
    &lt;/div&gt;
  );
}

答案2

得分: 0

> 我不明白为什么在调用 dispatchCherry() 后,cherryRef.current 会返回一个旧值,理论上它应该返回递增的值,因为 myRef 是一个对象。

是的,它确实返回了递增的值,但由于它尚未完成重新渲染的触发,所以不会立即显示给您(因为 useRef 不完全是状态变量)。可以使用 useState/useReducer 钩子来手动触发,或者您可以将 myRef 更改为状态变量。

例如,这可以给您下一个更新的值:

dispatch({ type: "add" });
setTimeout(() => {
  console.log(cherryRef.current); // 输出 1
}, 0);
英文:

> I don't understand why would cherryRef.current return an old value
> after calling dispatchCherry(). In theory it should return the
> incremented one, as myRef is an object.

Yes, it does return the incremented value, but since it does not complete the trigger of a re-render yet, it is not shown to you (because useRef is not exactly a state variable). The useState/useReducer hook can be used to manually trigger, or you can change myRef to a state variable.

Example, this can give you next updated value:

dispatch({ type: &quot;add&quot; });
setTimeout(() =&gt; {
  console.log(cherryRef.current); // Logs 1
}, 0);

huangapple
  • 本文由 发表于 2023年6月12日 02:25:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76451936.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定