Combining useState and useRef in ReactJS - reactjs

I have some variables of which I need the reference as well as the state. I found something here, that helped me: https://stackoverflow.com/a/58349377/7977410
Pretty much it just kept two variables synchronized (in pseudo code):
const [track, setTrack] = useState('INIT');
const trackRef = useRef('INIT');
// Whenever changing, just both are updated
setTrack('newValue');
trackRef.current = 'newValue';
I was wondering if it was beneficial to combine those two into a new hook. Something like this:
const useStateRef(initialState: any) {
const [state, setState] = useState<typeof initialState>(initialState);
const ref = useRef<typeof initialState>(initialState);
useEffect(() => {
setState(ref);
}, [ref]);
return [state, ref];
}
What would be the best way to do this? Is it even critical to do something like this?
(The background is, I have some self-repeating functions, that need the reference to change what they are doing. But I also need the state variable to rerender visible changes when the same variables are changing... Maybe there is a completely different way to do this, but I am still curious about this approach.)

It's possible, but to make the typings proper, you should use generics instead of any, and the effect hook needs to be reversed - change the ref.current when the state changes. You'll also want to return the state setter in order to change the value in the consumer of useStateRef.
const useStateRef = <T extends unknown>(initialState: T) => {
const [state, setState] = useState(initialState);
const ref = useRef(initialState);
useEffect(() => {
ref.current = state;
}, [state]);
// Use "as const" below so the returned array is a proper tuple
return [state, setState, ref] as const;
};
Or, to update synchronously, remove the effect hook:
if (ref.current !== state) ref.current = state;
Also note that there should never be any need to have a ref that only ever contains a primitive. const trackRef = useRef('INIT'); can be refactored away entirely and replaced with track. Refs are generally useful when dealing with objects, like HTMLElements.

Related

React - state not matching initial state [duplicate]

I'm refactoring to use Hooks and I've hit a very confusing wall
I have a basic functional component like so:
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
console.log(value, val); // abc undefined
return (...)
}
The log returns abc undefined - i.e. value in props is definitely defined, but the first argument returned from useState(value) is undefined
Just to test that hooks were working at all, I tried useState("abc") and that logs abc as expected.
I have no idea what I'm doing wrong - any ideas?
React version: 16.8.6
EDIT here is the parent component - nothing fancy going on here as far as I can see!
<MakeComponent
path={key}
value={item[key]}
info={value}
update={updateDiffs}
/>
As it is alluded to in the comments, useState(initialState) (or whatever you call it) will only use initialState on the first render.
During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState).
(React Docs, emphasis mine)
After this, on re-renders the useState function does not change the state based on new changes to the props passed in.
To make changes reflected everytime value changes, register an effect on the input prop like so
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
useEffect(() => { setVal(value)}, [value] )
return (...)
}
Note that just setting state based on the props changing is a bit of an anti-pattern, as MonsterBucket notices you could just rely directly on the props changing to trigger a re-render:
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
if (val !== value) { // don't update unnecessarily
setVal(value);
}
return (...)
}
And instead reserve useEffect for side effects, mostly those outside of the React render cycle.
To see examples of these, have a look as these ReactJs docs - You might not need an effect, which covers lots of other examples.
Here you have to add useEffect if you want to update the state on props change, which will listen to prop change & will update the state value accordingly
here is the updated snippet
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
useEffect(() => { setVal(value)}, [value] )
return (<div>{val}</div>)
}
Attching sandbox link
https://codesandbox.io/s/confident-agnesi-ohkq7?file=/src/MakeComponent.js
By the time you pass the prop value to useState the value of it can be yet to set. value itself might have been undefined yet.
Also setState is not truly sync so if the useState uses same mechanism as setState your state val might not be set to value yet as the initial value.
In such cases, using props as the initial values to states, you should use a side effect with no dependency. Once the first render achieved the effect will run and set your state with prop. You can let the initial value of the component be undefined passing nothing with no problems.
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState();
// get initial state after initial render
useEffect(() => {
setVal(value)
}, [])
console.log(value, val); // abc undefined then will log abc abc
return (...)
}
Just keep in mind that props in React are meant to be read-only, state is for read and write. But it is perfectly fine, and no not an anti pattern, if you use a prop just as an initial value for a state and use the state you have set with the prop after that instead of the prop. That is for consistency since you might have two different values at a time from a prop and a state in circumstances.
Your case might need to care for the value of the prop at an arbitrary time depending on you needs as stressed in one of the above answers. You question does not state that. Still, if so, you can add the prop to the dependency array of the effect that sets the state with that prop, and yes write separate effects for other props you want the same, well, effect.
If you don't need writing for that data you do not need that pattern at all, just use prop and it will be up to date and you will have consistency. But you apparently do so I hope the pattern I suggest above works for you as it does for me.

React Hooks: Is more performant to use `useRef` over `useState` to keep track of the boolean toggle state?

Overview
I'm implementing a useToggle react hook and I want to know if it is more performant to use useRef over useState to keep track of the boolean toggle state. Since useRef doesn't cause re-renders, this should be more efficient right?
useToggle (Using useRef)
const useToggle = () => {
const stateRef = useRef<boolean>(false);
const toggle = useCallback(() => stateRef.current = !stateRef.current, []);
return [stateRef, toggle];
}
useToggle (Using useState)
const useToggle = () => {
const [state, setState] = useState<boolean>(false);
const toggle: React.Dispatch<React.SetStateAction<boolean>> = useCallback(() => setState(state => !state), []);
return [state, toggle];
}
I think, its fine to use useRef until you don't want any re-renders i.e. any UI changes.
But I do agree to what #Mario said.
Though, you can prevent re-renders using useRef but it doesn't mean you should use useRef instead of useState.
You should try to minimize the use of states.
Here you haven't mentioned what useToggle is for; thats y its hard to say whether you should use it or not.
And we generally don't compare them as none of then can replace each other; both of them are made for different use cases.
This blog will give you an idea of when to use what.
I am not sure about the performance but, to be honest, my approach would be the useState one. The reason behind it is simply the one you gave: It causes updates.
What is the point on creating a useToggle hook that returns the state of the toggle if you do not want to use it to update something?

React useState(0) vs useState( () => 0 )

I saw React codes generally use arrow function for setState(). However, for useState(), I see that they just put a number, eg useState(0) instead of arrow function, ie useState( () => 0 )
Are there any reasons why I would want to use an arrow function in useState ? Is it alright to use only arrow functions for ALL React hook regardless, including initialising the hook. Any gotcha ? Thanks very much.
For your simple case it does not matter. You are just making an extra function call which returns a value without any computation.
But in case your initial state is a result of an expensive computation you can use a function.
Using the below
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
instead of
const [state, setState] = useState(someExpensiveComputation(props));
matters greatly.
The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded.
So even when your value is disregarded you would be making that expensive computation. You can provide a function in your useState which will only run on the very first render, like in the second case above. [Link](The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded.)
As far as I know, the arrow functions in state setter permit use the previous state.
For example:
const [message, setMessage] = useState('hello ')
... # Receive data from a form or API, etc.
let name = 'Richard'
setMessage(old_state => old_state + name) # 'hello Richard'
This way, you can create counter or reuse some data of the previous state.
Use setState(new_vale) is totally correct for reset the value.

Avoid function to run again and again in ReactJs

I created component which take props and this component filter the props but whenever state is changed it re-assign the variable.
Example:
const Foo = (props) => {
const [state, setState] = useState();
const schema = array_filter(props.data, "schema");
// other Code
}
Everytime when state is changed array_filter method is called. array_filter function is custom function which filter the array. I want to avoid extra running of array_filter function. How can I do that can you please let me know.
I try useRef but not working.
Just cache your result and it only re-calculates as your input is changed which is best in your case:
const schema = React.useMemo(() => array_filter(props.data, "schema"), [props.data]);

React - useState not setting initial value

I'm refactoring to use Hooks and I've hit a very confusing wall
I have a basic functional component like so:
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
console.log(value, val); // abc undefined
return (...)
}
The log returns abc undefined - i.e. value in props is definitely defined, but the first argument returned from useState(value) is undefined
Just to test that hooks were working at all, I tried useState("abc") and that logs abc as expected.
I have no idea what I'm doing wrong - any ideas?
React version: 16.8.6
EDIT here is the parent component - nothing fancy going on here as far as I can see!
<MakeComponent
path={key}
value={item[key]}
info={value}
update={updateDiffs}
/>
As it is alluded to in the comments, useState(initialState) (or whatever you call it) will only use initialState on the first render.
During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState).
(React Docs, emphasis mine)
After this, on re-renders the useState function does not change the state based on new changes to the props passed in.
To make changes reflected everytime value changes, register an effect on the input prop like so
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
useEffect(() => { setVal(value)}, [value] )
return (...)
}
Note that just setting state based on the props changing is a bit of an anti-pattern, as MonsterBucket notices you could just rely directly on the props changing to trigger a re-render:
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
if (val !== value) { // don't update unnecessarily
setVal(value);
}
return (...)
}
And instead reserve useEffect for side effects, mostly those outside of the React render cycle.
To see examples of these, have a look as these ReactJs docs - You might not need an effect, which covers lots of other examples.
Here you have to add useEffect if you want to update the state on props change, which will listen to prop change & will update the state value accordingly
here is the updated snippet
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
useEffect(() => { setVal(value)}, [value] )
return (<div>{val}</div>)
}
Attching sandbox link
https://codesandbox.io/s/confident-agnesi-ohkq7?file=/src/MakeComponent.js
By the time you pass the prop value to useState the value of it can be yet to set. value itself might have been undefined yet.
Also setState is not truly sync so if the useState uses same mechanism as setState your state val might not be set to value yet as the initial value.
In such cases, using props as the initial values to states, you should use a side effect with no dependency. Once the first render achieved the effect will run and set your state with prop. You can let the initial value of the component be undefined passing nothing with no problems.
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState();
// get initial state after initial render
useEffect(() => {
setVal(value)
}, [])
console.log(value, val); // abc undefined then will log abc abc
return (...)
}
Just keep in mind that props in React are meant to be read-only, state is for read and write. But it is perfectly fine, and no not an anti pattern, if you use a prop just as an initial value for a state and use the state you have set with the prop after that instead of the prop. That is for consistency since you might have two different values at a time from a prop and a state in circumstances.
Your case might need to care for the value of the prop at an arbitrary time depending on you needs as stressed in one of the above answers. You question does not state that. Still, if so, you can add the prop to the dependency array of the effect that sets the state with that prop, and yes write separate effects for other props you want the same, well, effect.
If you don't need writing for that data you do not need that pattern at all, just use prop and it will be up to date and you will have consistency. But you apparently do so I hope the pattern I suggest above works for you as it does for me.

Resources