Add `get` function to `useState`
See original GitHub issueDo you want to request a feature or report a bug?
- feature
What is the current behavior? Code from Introducing Hooks:
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
// each time "count" changed, this arrow function will be created again.
// so that it can access the latest "count"
onClick={() => setCount(count + 1)}
I don’t think it is good to create a fixed function many times, so I try to modify the code:
(Update on Jul 2022: No matter using the inline anonymous function or wrapping with useCallback
, the function will always be created. The difference is that, in useCallback
approach, the function reference will not be changed, which could be helpful if we use memo
to wrap the component who receives the function as a property)
const [count, setCount] = useState(0);
const handleClick = useCallback(() => setCount(count + 1), []);
But obviously the callback in useCallback
couldn’t get the latest count
because I pass in an empty inputs array to avoid this callback been generated again and again.
So, in fact, the inputs array decide two things:
- when to recreate the callback
- which state can be accessed in the callback
In most situation, the two things are one thing, but here they conflict.
So I think maybe it’s good to add a get
function to useState
like this:
import { useState, useCallback } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount, getCount] = useState(0);
const handleClick = useCallback(() => setCount(getCount() + 1), []);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>
Click me
</button>
</div>
);
}
Maybe it’s confusing because getCount
can totally replace count
, but it brings the possible to avoid creating callbacks again and again.
Edited
https://github.com/facebook/react/issues/14543#issuecomment-452237355 exactly resolves the case above. But there‘re many other scenarios can’t use updater
to resolve. Here are some more code snippets:
1. Access states in a timer.
useEffect(() => {
// or setInterval
const id = setTimeout(() => {
// access states
}, period);
return () => clearTimeout(id);
}, inputs);
2. Access states in WebSocket callbacks
useEffect(() => {
// create a WebSocket client named "ws"
ws.onopen = () => {
// access states
};
ws.onmessage = () => {
// access states
};
return () => ws.close();
}, inputs);
3. Access states in Promise
useEffect(() => {
create_a_promise().then(() => {
// access states
});
}, inputs);
4. Access states in event callbacks
useEffect(() => {
function handleThatEvent() {
// access states
}
instance.addEventListener('eventName', handleThatEvent);
return instance.removeEventListener('eventName', handleThatEvent);
}, inputs);
We had to use some workaround patterns to resolve those cases, like https://github.com/facebook/react/issues/14543#issuecomment-452676760 https://github.com/facebook/react/issues/14543#issuecomment-453058025 https://github.com/facebook/react/issues/14543#issuecomment-453079958 Or a funny way:
const [state, setState] = useState();
useEffect(() => {
// or setInterval
const id = setTimeout(() => {
// access states
setState((prevState) => {
// Now I can do anything with state...🤮
...
return prevState;
});
}, period);
return () => clearTimeout(id);
}, inputs);
So let’s discuss and wait… https://github.com/facebook/react/issues/14543#issuecomment-452713416
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn’t have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
What is the expected behavior?
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
- React 16.7.0-alpha.2
Issue Analytics
- State:
- Created 5 years ago
- Reactions:10
- Comments:33 (2 by maintainers)
@liyuanqiu you can use updater function in setCount
const handleClick = useCallback(() => setCount(prevCount => prevCount + 1), []);
useState
API referenceI really like the suggestion for having pair of getter and setter returned from
useState
… which would make it easier to keep things fresh… if it doesn’t end up in the official implementation, I think it can be implemented in user land using custom hook like:?