score:422
you can write a custom hook to provide you a previous props using useref
function useprevious(value) {
const ref = useref();
useeffect(() => {
ref.current = value;
});
return ref.current;
}
and then use it in useeffect
const component = (props) => {
const {receiveamount, sendamount } = props
const prevamount = useprevious({receiveamount, sendamount});
useeffect(() => {
if(prevamount.receiveamount !== receiveamount) {
// process here
}
if(prevamount.sendamount !== sendamount) {
// process here
}
}, [receiveamount, sendamount])
}
however its clearer and probably better and clearer to read and understand if you use two useeffect
separately for each change id you want to process them separately
score:-2
in your case(simple object):
useeffect(()=>{
// your logic
}, [rate, sendamount, receiveamount])
in other case(complex object)
const {cityinfo} = props;
useeffect(()=>{
// some logic
}, [cityinfo.cityid])
score:0
you can use useimmer opposed to usestate and access the state. example: https://css-tricks.com/build-a-chat-app-using-react-hooks-in-100-lines-of-code/
score:0
i did not like any of the answers above, i wanted the ability to pass an array of booleans and if one of them is true so rerender
/**
* effect fires if one of the conditions in the dependency array is true
*/
export const useeffectcompare = (callback: () => void, conditions: boolean[], effect = useeffect) => {
const shouldupdate = useref(false);
if (conditions.some((cond) => cond)) shouldupdate.current = !shouldupdate.current;
effect(callback, [shouldupdate.current]);
};
//usage - will fire because one of the dependencies is true.
useeffectcompare(() => {
console.log('test!');
}, [false, true]);
score:2
be careful with most voted answers. for more complex scenarios above variations of useprevious
can give you too much re-renders (1) or the same value as original (2).
we have to:
- add
[value]
as dependency inuseeffect
to re-run only ifvalue
changes - assign
json.parse(json.stringify(value))
(or some deep copy) toref.current
insindeuseeffect
to prevent passing the reference of state to ref instead of the value
upgraded hook:
const useprevious = <t>(value: t): t => {
const ref: any = useref<t>()
useeffect(() => {
ref.current = json.parse(json.stringify(value))
}, [value])
return ref.current
}
score:3
using ref will introduce a new kind of bug into the app.
let's see this case using useprevious
that someone commented before:
- prop.mintime: 5 ==> ref.current = 5 | set ref.current
- prop.mintime: 5 ==> ref.current = 5 | new value is equal to ref.current
- prop.mintime: 8 ==> ref.current = 5 | new value is not equal to ref.current
- prop.mintime: 5 ==> ref.current = 5 | new value is equal to ref.current
as we can see here, we are not updating the internal ref
because we are using useeffect
score:4
for really simple prop comparison you can use useeffect
to easily check to see if a prop has updated.
const mycomponent = ({ prop }) => {
useeffect(() => {
---do stuffhere----
}, [prop])
}
useeffect
will then only run your code if the prop changes.
score:4
here's a custom hook that i use which i believe is more intuitive than using useprevious
.
import { useref, useeffect } from 'react'
// usetransition :: array a => (a -> void, a) -> void
// |_______| |
// | |
// callback deps
//
// the usetransition hook is similar to the useeffect hook. it requires
// a callback function and an array of dependencies. unlike the useeffect
// hook, the callback function is only called when the dependencies change.
// hence, it's not called when the component mounts because there is no change
// in the dependencies. the callback function is supplied the previous array of
// dependencies which it can use to perform transition-based effects.
const usetransition = (callback, deps) => {
const func = useref(null)
useeffect(() => {
func.current = callback
}, [callback])
const args = useref(null)
useeffect(() => {
if (args.current !== null) func.current(...args.current)
args.current = deps
}, deps)
}
you'd use usetransition
as follows.
usetransition((prevrate, prevsendamount, prevreceiveamount) => {
if (sendamount !== prevsendamount || rate !== prevrate && sendamount > 0) {
const newreceiveamount = sendamount * rate
// do something
} else {
const newsendamount = receiveamount / rate
// do something
}
}, [rate, sendamount, receiveamount])
hope that helps.
score:5
since state isn't tightly coupled with component instance in functional components, previous state cannot be reached in useeffect
without saving it first, for instance, with useref
. this also means that state update was possibly incorrectly implemented in wrong place because previous state is available inside setstate
updater function.
this is a good use case for usereducer
which provides redux-like store and allows to implement respective pattern. state updates are performed explicitly, so there's no need to figure out which state property is updated; this is already clear from dispatched action.
here's an example what it may look like:
function reducer({ sendamount, receiveamount, rate }, action) {
switch (action.type) {
case "sendamount":
sendamount = action.payload;
return {
sendamount,
receiveamount: sendamount * rate,
rate
};
case "receiveamount":
receiveamount = action.payload;
return {
sendamount: receiveamount / rate,
receiveamount,
rate
};
case "rate":
rate = action.payload;
return {
sendamount: receiveamount ? receiveamount / rate : sendamount,
receiveamount: sendamount ? sendamount * rate : receiveamount,
rate
};
default:
throw new error();
}
}
function handlechange(e) {
const { name, value } = e.target;
dispatch({
type: name,
payload: value
});
}
...
const [state, dispatch] = usereducer(reducer, {
rate: 2,
sendamount: 0,
receiveamount: 0
});
...
score:7
i just published react-delta which solves this exact sort of scenario. in my opinion, useeffect
has too many responsibilities.
responsibilities
- it compares all values in its dependency array using
object.is
- it runs effect/cleanup callbacks based on the result of #1
breaking up responsibilities
react-delta
breaks useeffect
's responsibilities into several smaller hooks.
responsibility #1
useprevious(value)
uselatest(value)
usedelta(value, options)
usedeltaarray(valuearray, options)
usedeltaobject(valueobject, options)
some(deltaarray)
every(deltaarray)
responsibility #2
in my experience, this approach is more flexible, clean, and concise than useeffect
/useref
solutions.
score:7
if you prefer a useeffect
replacement approach:
const usepreviouseffect = (fn, inputs = []) => {
const previousinputsref = useref([...inputs])
useeffect(() => {
fn(previousinputsref.current)
previousinputsref.current = [...inputs]
}, inputs)
}
and use it like this:
usepreviouseffect(
([prevreceiveamount, prevsendamount]) => {
if (prevreceiveamount !== receiveamount) // side effect here
if (prevsendamount !== sendamount) // side effect here
},
[receiveamount, sendamount]
)
note that the first time the effect executes, the previous values passed to your fn
will be the same as your initial input values. this would only matter to you if you wanted to do something when a value did not change.
score:35
going off the accepted answer, an alternative solution that doesn't require a custom hook:
const component = ({ receiveamount, sendamount }) => {
const prevamount = useref({ receiveamount, sendamount }).current;
useeffect(() => {
if (prevamount.receiveamount !== receiveamount) {
// process here
}
if (prevamount.sendamount !== sendamount) {
// process here
}
return () => {
prevamount.receiveamount = receiveamount;
prevamount.sendamount = sendamount;
};
}, [receiveamount, sendamount]);
};
this assumes you actually need reference to the previous values for anything in the "process here" bits. otherwise unless your conditionals are beyond a straight !==
comparison, the simplest solution here would just be:
const component = ({ receiveamount, sendamount }) => {
useeffect(() => {
// process here
}, [receiveamount]);
useeffect(() => {
// process here
}, [sendamount]);
};
score:59
option 1 - run useeffect when value changes
const component = (props) => {
useeffect(() => {
console.log("val1 has changed");
}, [val1]);
return <div>...</div>;
};
option 2 - usehaschanged hook
comparing a current value to a previous value is a common pattern, and justifies a custom hook of it's own that hides implementation details.
const component = (props) => {
const hasval1changed = usehaschanged(val1)
useeffect(() => {
if (hasval1changed ) {
console.log("val1 has changed");
}
});
return <div>...</div>;
};
const usehaschanged= (val: any) => {
const prevval = useprevious(val)
return prevval !== val
}
const useprevious = (value) => {
const ref = useref();
useeffect(() => {
ref.current = value;
});
return ref.current;
}
score:111
incase anybody is looking for a typescript version of useprevious:
in a .tsx
module:
import { useeffect, useref } from "react";
const useprevious = <t extends unknown>(value: t): t | undefined => {
const ref = useref<t>();
useeffect(() => {
ref.current = value;
});
return ref.current;
};
or in a .ts
module:
import { useeffect, useref } from "react";
const useprevious = <t>(value: t): t | undefined => {
const ref = useref<t>();
useeffect(() => {
ref.current = value;
});
return ref.current;
};
Source: stackoverflow.com
Related Query
- How to compare oldValues and newValues on React Hooks useEffect?
- How to compare oldValues and newValues on React Hooks useEffect? Multiple re-renders
- How to execute store.unsubscribe from useEffect using React hooks and Redux
- How to test useState and useEffect react hooks
- How to use setTimeout() along with React Hooks useEffect and setState?
- How to test React useEffect hooks with jasmine and enzyme
- How do I correctly add data from multiple endpoints called inside useEffect to the state object using React Hooks and Context API?
- How to Make A second API call based on the value gotten from the first. with React and useEffect hooks
- How to prevent child component from re-rendering when using React hooks and memo?
- How to fix nextCreate is not a function setting up useMemo setting up authentication react router and hooks
- How to clean up setInterval in useEffect using react hooks
- When and how to choose between React Hooks and old HOC props passing?
- ES6 React - What are the difference between reference, shallow copy and deep copy and how to compare them?
- How to compare values from react redux state in hooks
- How can React useEffect watch and update state?
- Access old state to compare with new state inside useEffect react hook with custom hooks usePrevious
- How to fix React Redux and React Hook useEffect has a missing dependency: 'dispatch'
- How to perform authentication with React hooks and react-router
- How to export function and import react hooks
- How to fetch data without useEffect hooks in React function component?
- How to handle React Svg Drag and Drop with React Hooks
- I have read the React Docs on Hooks and I'm confused. When is the useEffect Hook clean up function called?
- useEffect how to compare previous and updated props
- In react with hooks and bootstrap, how do I change chevron as accordion opens?
- How to implement Jest test for Axios and Hooks - useEffect
- How can I use useEffect in React to run some code only when the component first mounts, and some other code whenever an event occurs (repeatedly)?
- How can show and hide react components using hooks
- React hooks useEffect call API second time and first time calling API response also returning
- How to fix my React Context to render useEffect and reload table data
- Difference and use cases with useEffect and useMemo hooks on React
More Query from same tag
- Uploading a React-rendered SVG
- Can't import Google Fonts with Styled Components and Next.js
- Mocking functions called inside then() with jest
- Why can't we invoke ReactDOM or React directly in the browser?
- How do I execute a function passed as props between components in React?
- App bar menu items not opening correct sub menu
- React Redux source function ensureCanMutateNextListeners?
- How to change background color of drawer component materialui in react?
- How can I remove last shape layer when creating new one in lefalet
- How to change SVG icon color on select in ToggleButton
- Strategy for serving OAuth access token within Redux application
- unable to load react component in index.html
- Warning: Can't perform a React state update on an unmounted component. In a functional component
- How to send React Component as props
- How to pass state data from custom hooks to react component?
- react-three-fiber rotate around a certain axis
- How to remove only one item from the LocalStorage onClick?
- react + webpack failing on difference machine
- Unable to retrieve and manipulate CSS in React js
- How to change font family dynamically in a functional component?
- React FontAwesome Icon disappears when I add <a> tag
- Set React component state from outside of component
- Using material-UI's Grid component, how do I nest a table within another table?
- Trying to change the img src from an array on click using react
- Unshuffle the questions on javascript
- Firestore composite query pagination
- React CSS Style is getting overridden
- Access selected value from react's Select Component
- Slug that isn't whole file name in Next.js?
- Cannot bind context values to setInterval function Reactjs