React has a hook called useState, which is used when adding state to functional components.
The Hooks API Reference states:
useState:
const [state, setState] = useState(initialState);Returns a stateful value, and a function to update it.
During the initial render, the returned state (
state) is the same as the value passed as the first argument (initialState).The
setStatefunction is used to update the state. It accepts a new state value and enqueues a re-render of the component.
The React Documentation states:
What do we pass to
useStateas an argument?The only argument to the
useState()Hook is the initial state. Unlike with classes, the state doesn’t have to be an object. We can keep a number or a string if that’s all we need. In our example, we just want a number for how many times the user clicked, so pass0as initial state for our variable. (If we wanted to store two different values in state, we would calluseState()twice.)
Unexpected behaviour:
However, I've noticed some strange, seemingly undocumented, behaviour.
If I try to use the useState hook to store a function as state, react will invoke the function reference. e.g.
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...
};
When we call useState(arbitraryFunction), react will invoke arbitraryFunction and use its return value as the state.
As a workaround:
We can store functions as state by wrapping our function reference in another function. e.g.
const [fn, setFn] = useState(() => arbitraryFunction)
I haven't yet come across any real-world reasons to store functions as state, but it seems weird that somebody made the explicit choice to treat function arguments differently.
This choice can be seen in multiple places throughout the React codebase:
initialState = typeof initialArg === 'function' ? initialArg() : initialArg;
Why does this seemingly undocumented feature exist?
I can't think of any good reasons why anybody would want/expect their function reference to be invoked, but perhaps you can.
If this is documented, where is it documented?