为什么'useState'钩子在引用函数时会调用初始状态?

时间:2020-01-30 10:24:21

标签: javascript reactjs react-hooks

React有一个称为useState的钩子,该钩子在向功能组件添加状态时使用。

Hooks API Reference指出:

useState

const [state, setState] = useState(initialState);

返回有状态值,以及更新它的函数。

在初始渲染期间,返回状态(state)与作为第一个参数(initialState)传递的值相同。

setState函数用于更新状态。它接受一个新的状态值并排队重新呈现该组件。

React Documentation指出:

我们将传递给useState作为参数吗?

useState()挂钩的唯一参数是初始状态。与类不同,状态不必是对象。如果需要,我们可以保留一个数字或一个字符串。在我们的示例中,我们只需要一个用户点击次数的数字,因此将0作为变量的初始状态。 (如果我们想在状态中存储两个不同的值,我们将调用useState()两次。)

意外行为:

但是,我注意到一些奇怪的,似乎没有记录的行为。

如果我尝试使用useState钩子将函数存储为状态,则 react将调用函数引用。例如

const arbitraryFunction = () => {
    console.log("I have been invoked!");
    return 100;
};

const MyComponent = () => {

    // Trying to store a string - works as expected:
    const [website, setWebsite] = useState("stackoverflow"); // Stores the string
    console.log(typeof website);                             // Prints "string"
    console.log(website);                                    // Prints "stackoverflow"

    // Trying to store a function - doesn't work as expected:
    const [fn, setFn] = useState(arbitraryFunction);         // Prints "I have been invoked!"
    console.log(typeof fn);                                  // Prints "number" (expecting "function")
    console.log(fn);                                         // Prints "100"

    return null; // Don't need to render anything for this example...
};

当我们调用useState(arbitraryFunction)时,react将调用arbitraryFunction并将其返回值用作状态。

作为解决方法:

我们可以通过将函数引用包装在另一个函数中来将函数存储为状态。例如

const [fn, setFn] = useState(() => arbitraryFunction)

我还没有遇到任何现实的理由将函数存储为状态,但是似乎很奇怪,有人做出了明确的选择以不同的方式对待函数参数。

在整个React codebase的多个地方都可以看到此选择:

initialState = typeof initialArg === 'function' ? initialArg() : initialArg;

为什么此看似未记录的功能存在?

我想不出为什么有人希望/期望调用他们的函数引用的任何充分理由,但是也许可以。

如果已记录,在哪里记录?

1 个答案:

答案 0 :(得分:8)

这已记录在here中:

惰性初始状态

initialState参数是初始渲染期间使用的状态。 在后续渲染中,将忽略它。如果初始状态是 昂贵的计算结果,您可以提供一个函数 而是仅在初始渲染上执行:

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

将回调传递给setState also calls the callback, but for a different reason

功能更新

如果新状态是使用先前状态计算的,则可以传递一个 函数为setState。该函数将接收先前的值, 并返回更新的值。这是计数器组件的示例 使用两种形式的setState:

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}