-
Notifications
You must be signed in to change notification settings - Fork 569
Introduce a new hook that called useStateRef #201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
29ff8e6 to
e81cdf7
Compare
|
Nice. Encouraging the use of getters can also prevent issues like this from happening more often: https://reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function (But, of course, may result in other type of bugs and mis-assumptions to appear more often, like when you intentionally want to see the value of state when you triggered an action, and not the current value) See this for example: Also from syntax point of view, hooks removed |
|
How do you think of And I think Chances are that I need call a function but don't really want the function change trigger the calling: As I mentioned in facebook/react#22773, the real problem we are talking here is
|
I've started by copying the Yarn RFCs repo, with some tweaks to the README and the RFC template. We'll continue iterating on these.
|
Just had this same idea and I'm so glad someone already open an RFC for it. const [count, setCount, getCount] = React.useStateRef(0);
const handleClick = useCallback(() => {
setCount((c) => c + 1);
console.log(getCount()); // this would print 0 as the state doesn't update sincronously
}, []);I'm not familiar with react internal code, but I imagine it keeps a stack of all recent calls to setState. I believe such a stack could be accessed and also executed (in the case an update used a callback) to get the latest state. |
|
@viniciusdacal Thank you, your consideration is perfect, but the purpose of this is to remove dependencie from const [a, setA] = useState(0);
const [b, setB] = useState(0);
const handleClick = useCallback(() => {
setA(a + 1); // a is 0 here
setB(a + b); // a is 0 here yet
console.log(a, b); // 0 0
}, [a, b]);so we want to remove dependencie from above code: const [a, setA, getA] = useStateRef(0);
const [b, setB, getB] = useStateRef(0);
const handleClick = useCallback(() => {
setA(getA() + 1); // getA() returns 0 here
setB(getA() + getB()); // getA() returns 0 here yet as above code
console.log(getA(), getB()); // 0 0
}, []);as you see we didn't change behavior we just prevent re-initiate |
|
@aghArdeshir Thanks, yes I agree with you on some points, I tried to prevent breaking-changes. |
|
@badeggg Thank you for the comment, as you see this hook could be embedded to |
e20a0e7 to
0dfb3fe
Compare
|
I use something like: import {
useState,
useRef,
useMemo,
useCallback,
useEffect,
SetStateAction,
Dispatch,
} from "react";
export const useStateRef = <S>(
initialState: S | (() => S)
): [S, Dispatch<SetStateAction<S>>, () => S] => {
const [, updateRenderState] = useState(0);
const stateRef = useRef<S>(
useMemo(
initialState instanceof Function ? initialState : () => initialState,
[]
)
);
useEffect(
() => () => {
stateRef.current = null as S; // Just in case help to free some memory
},
[]
);
return [
stateRef.current,
useCallback<Dispatch<SetStateAction<S>>>((v) => {
stateRef.current = v instanceof Function ? v(stateRef.current) : v;
updateRenderState((v) => (v + 1) % Number.MAX_SAFE_INTEGER);
}, []),
useCallback(() => stateRef.current, []),
];
};In this implementation PS. This is a simplified version - as a lazy programmer I handle a bit more useful cases like: import {
useState,
useRef,
useMemo,
useCallback,
useEffect,
SetStateAction,
Dispatch,
} from "react";
const INI = Object.create(null);
export const useSmartState = <S>(
initialState: S | (() => S),
updateStateOnInitialStateChange = false
): [S, Dispatch<SetStateAction<S>>, () => S] => {
const [, updateRenderState] = useState(0);
const stateRef = useRef<S>(INI as S);
useMemo(() => {
stateRef.current =
stateRef.current === INI || updateStateOnInitialStateChange
? initialState instanceof Function
? initialState()
: initialState
: stateRef.current;
}, [initialState, updateStateOnInitialStateChange]);
useEffect(
() => () => {
stateRef.current = null as S;
},
[]
);
return [
stateRef.current,
useCallback<Dispatch<SetStateAction<S>>>((v) => {
const current = stateRef.current;
stateRef.current = v instanceof Function ? v(current) : v;
if (stateRef.current !== current) {
updateRenderState((v) => (v + 1) % Number.MAX_SAFE_INTEGER);
}
}, []),
useCallback(() => stateRef.current, []),
];
}; |
|
We have posted an alternative proposal that we think achieves the same goals (but differently). Feedback welcome! |
Summary
Introduce a new hook to reduce/improve some processes.
Basic example
In this example we see
useEffectthat doesn't need a dependency to read updatedcount's value, so that's mean we don't need to performuseEffecteffect function eventcountwill change.useStateRefjust is a name, so might we need to change to a better name.Also, in this example,
useCallbackdoesn't need a dependency to knowcount's value, souseCallbackinside function just performs once. that's awesomeMotivation
useEffectanduseCallbackneed to know dependencies to redefinefunctiondepended on new values of dependencies. but these are redundant process forReactbecause we don't know what time we need it exactly ex:handleClickdepend on user behavior so we just needcount's value when the user needs it.Detailed design
A basic of implementation for
useStateRefthat we can use in project is:TS version:
JS version:
Drawbacks
This is a new hook, so we don't have any breaking change, also we can implement that by internal React hooks.
Alternatives
Alternative can be a package, maybe
Adoption strategy
Fortunately, we don't have any breaking change, also we can embed this hook to
useStatewithout breaking changeHow we teach this
This RFC introduce a new hook, so we have to add some section to React documentation
Unresolved questions
useState?