How to get previoius state value in react hook?
Here is my code. I used useRef hook also but didnot work.
const [ category, setCategory] = useState([]);
function updateMarkers(data) {
let newCategory= [];
let {name, age} = data.payload;
//Here need to check previous cateogory has same age or not.
category && category.map((cate, i) => {
if (cate.age!== age) {
newCategory[i] = data.payload
}
})
}
setCategory((prevCategory) => [...prevCategory, ...newCategory]) }
let prevCategory = usePrevious(category);
console.log(' prevcategory', prevCategory);
You can create usePrevious like this:
export default function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
And using:
const previousState = usePrevious(state)
you can access previous state doing the following
setState(prevState => prevState)
Related
I want to detect which of the argument props have changed inside my use effect. How can I achieve this? I need something like this:
const myComponent = () => {
...
useEffect(() => {
if (prop1 === isTheOneThatChangedAndCusedTheTrigger){
doSomething(prop1);
}else{
doSomething(prop2);
}
},[prop1, prop2]);
};
export function myComponent;
While you can use something like usePrevious to retain a reference to the last value a particular state contained, and then compare it to the current value in state inside the effect hook...
const usePrevious = (state) => {
const ref = useRef();
useEffect(() => {
ref.current = state;
}, [value]);
return ref.current;
}
const myComponent = () => {
const [prop1, setProp1] = useState();
const prevProp1 = usePrevious(prop1);
const [prop2, setProp2] = useState();
const prevProp2 = usePrevious(prop2);
useEffect(() => {
if (prop1 !== prevProp1){
doSomething(prop1);
}
// Both may have changed at the same time - so you might not want `else`
if (prop2 !== prevProp2) {
doSomething(prop2);
}
},[prop1, prop2]);
};
It's somewhat ugly. Consider if there's a better way to structure your code so that this isn't needed - such as by using separate effects.
useEffect(() => {
doSomething(prop1);
}, [prop1]);
useEffect(() => {
doSomething(prop2);
}, [prop2]);
I recently started using useEffect hook, and I'm facing some issues. This is a simple App component that renders an input field and submit button. On hitting submit selectItem is called which in turn calls an async function getNames(). getNames function checks if there is an existing entry, if so it returns, otherwise it calls 3rd party API getNewNames() to get newNames. I tried setting the state with this newNames field, but it seems like it is undefined in first render. But after the first render it is defined. How do I make sure that I have newNames field, so that it doesn't return undefined in any renders?
const App = () => {
const [namesArr, setNamesArr] = useState([])
const [name, setName] = useState('')
useEffect (()=> {
console.log('Inside use Effect')
}, [namesArr])
const changeInput = (val) => {
setName(val)
}
const selectItem = async() => {
const returnedVal = await getNames()
// ReturnVal is empty in first render, but filled in second render.
/
}
const getNames = async() =>{
const existingNames = namesArr.find((name)=> name === name)
if(existingNames){
return 'We have an entry'
}
else{
console.log(`Names are not reloaded properly, need to re-render`)
const newNames = await getNewNames() // this is
setName((oldNames)=> [...oldNames, newNames])
return namesArr
}
}
return <div>
<input value={name} onChange={(e)=> changeInput(e.target.value)}></input>
<button onClick={()=> selectItem()}></button>
</div>
}
I think your problem is related to the setName method. In the line after setName console.log(namesArr) will be undefined. So how can we fix this?
const getNames = async() =>{
const existingNames = namesArr.find((name)=> name === name)
if(existingNames){
return 'We have an entry'
}
else{
const newNames = await getNewNames();
// We created our new list outside
// If x is a list, prepend an ellipsis ...newNames
const newList = [...namesArr, newNames];
// setName string is a state. Incorrect state updating.
// setNamesArr instead of setName
setNamesArr(newList)
return newList;
}
}
Now there are two possibilities here.
returnedVal === array or returnedVal === 'We have an entry'
const selectItem = async() => {
const returnedVal = await getNames();
console.log(returnedVal); // array or string.
}
I am using a context like the following:
const placeCurrentOrder = async () => {
alert(`placing order for ${mealQuantity} and ${drinkQuantity}`)
}
<OrderContext.Provider
value={{
placeCurrentOrder,
setMealQuantity,
setDrinkQuantity,
}}
>
and I'm calling this context deep down with something like this (when the user clicks a button):
const x = () => {
orderContext.setMealQuantity(newMealQuantity)
orderContext.setDrinkQuantity(newDrinkQuantity)
await orderContext.placeCurrentOrder()
}
Sort of like I expect, the state doesn't update in time, and I always get the previous value of the state. I don't want to have a useEffect, because I want control over exactly when I call it (for example, if mealQuantity and drinkQuantity both get new values here, I don't want it being called twice. The real function is far more complex.)
What is the best way to resolve this? I run into issues like this all the time but I haven't really gotten a satisfactory answer yet.
You can set them in a ref. Then use the current value when you want to use it. The easiest way is probably to just create a custom hook something like:
const useStateWithRef = (initialValue) => {
const ref = useRef(initialValue)
const [state, setState] = useState(initialValue)
const updateState = (newState) => {
ref.current = typeof newState === 'function' ? newState(state) : newState
setState(ref.current)
}
return [state, updateState, ref]
}
then in your context provider component you can use it like:
const [mealQuantity, setMealQuantity, mealQuantityRef] = useStateWithRef(0)
const [drinkQuantity, setDrinkQuantity, drinkQuantityRef] = useStateWithRef(0)
const placeOrder = () => {
console.log(mealQuantityRef.current, drinkQuantityRef.current)
}
You can also just add a ref specifically for the order and then just update it with a useEffect hook when a value changes.
const [drinkQuantity, setDrinkQuantity] = useState(0)
const [mealQuantity, setMealQuantity] = useState(0)
const orderRef = useRef({
drinkQuantity,
mealQuantity
})
useEffect(() => {
orderRef.current = {
drinkQuantity,
mealQuantity,
}
}, [drinkQuantity, mealQuantity])
const placeOrder = () => {
console.log(orderRef.current)
}
So I am re-writing a component with hooks, and I ran into an interesting challenge, I need to mimick some old behaviour of componentWillReceiveProps with the useEffect hook.
My old code looks like:
componentWillReceiveProps(nextProps: Props) {
const prevLateVal = get(`lateMinutes[${bookingId}].value`, this.props);
const nextLateVal = get(`lateMinutes[${bookingId}].value`, nextProps); //see here,
//we use next props
if (typeof nextLateVal !== 'undefined' && prevLateVal !== nextLateVal) {
client.connect(bookingId, nextLateVal === null ? 0 : nextLateVal);
}
}
You see, I am initiating a const based on nextProps, then in the if statement i do a couple checks based on that nextVal, now, I know that we can specify a second argument to useEffect to run it only when a prop changes, but what about those checks, how can i implement something similar to nextProps?
You can create custom hook:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.prevLateVal = value;
});
return ref.prevLateVal;
}
and get it used in useEffect()
const Component = (props) => {
const currentLateValue = get(`lateMinutes[${bookingId}].value`, props)
const prevLateVal = usePrevious(currentLateValue);
useEffect(() => {
if(prevLateVal !== currentLateValue) {
// process here
}
}, [currentLateValue]) // This will be executed only if currentLateValue changes.
}
You can use useRef to save the prev props, and use useEffect to run when props change, something like this :
function MyComponent(props) {
const prevProps = useRef(props);
useEffect(() => {
const prevLateVal = get(`lateMinutes[${bookingId}].value`, prevProps.current);
const nextLateVal = get(`lateMinutes[${bookingId}].value`, props);
if (typeof nextLateVal !== 'undefined' && prevLateVal !== nextLateVal) {
client.connect(bookingId, nextLateVal === null ? 0 : nextLateVal);
}
prevProps.current = props;
}, [props, bookingId]);
return (<div>...</div>)
}
With the current logic, you want to fire a side-effect only on lateMinutes[${bookingId}].value change:
const Component = props => {
useEffect(() => {
console.log('prop lateMinutes changed');
// ...
}, [props[`lateMinutes[${bookingId}].value`]);
return ...
};
You don’t need hooks to handle prerender lifecycle.
Just put the things in the functional component before returning JSX as the function itself is equivalent to render method of class based component.
With const [open, setOpen] = useState(false) I can create a variable open which is persisted over calls of a functional component.
But which hook can I use if I do not want a rerender when setting a variable?
I have a custom hook draft:
const useVariable = (initialValue) => {
const ref = useRef();
return useMemo(() => {
ref.current = [initialValue, (newValue) => { ref.current[0] = newValue }]
}, [])
}
But according to https://reactjs.org/docs/hooks-reference.html#usememo I can not rely that useMemo is not called anytime again.
You can make use of useRef hook if you just want to store the some data in a variable and not re-render when the variable is set
const unsubCallback = useRef(null);
if(!unsubCallback) {
unsubCallback.current = subscribe(userId)
}
Thank to #shubham-khatri I found a solution to my question. Just use the initialValue of the useRef hook:
const useVariable = initialValue => {
const ref = useRef([
initialValue,
param => {
ref.current[0] = typeof param === "function"
? param(ref.current[0])
: param
}
]);
return ref.current;
};
https://codesandbox.io/s/v3zlk1m90
Edit: To account for Christopher Camp's comment I added that also a function can be passed like in useState. See usage in codesandbox