React has a hook called useState, which is used when adding state to functional components.
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
setState
function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
What do we pass to
useState
as 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 pass0
as initial state for our variable. (If we wanted to store two different values in state, we would calluseState()
twice.)
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.
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;
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?
With the useState hook, you can pass a function as an argument to initialize the state lazily. As discussed, the initial value is needed only once at the first render. There is no point in performing this heavy computation on the subsequent renders.
To set a conditional initial value for useState in React:Pass a function to the useState hook. Use a condition to determine the correct initial value for the state variable. The function will only be invoked on the initial render.
We initialize our state by calling useState in our function component. useState accepts an initial state and returns two values: The current state. A function that updates the state.
The useState() is a Hook that allows you to have state variables in functional components . so basically useState is the ability to encapsulate local state in a functional component.
This is documented here:
Lazy initial state
The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded. If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render:
const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; });
Passing a callback to setState
also calls the callback, but for a different reason:
Functional updates
If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value. Here’s an example of a counter component that uses both forms of 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> </> ); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With