score:226
we can use the useref
hook to store any mutable value we like, so we could use that to keep track of if it's the first time the useeffect
function is being run.
if we want the effect to run in the same phase that componentdidupdate
does, we can use uselayouteffect
instead.
example
const { usestate, useref, uselayouteffect } = react;
function componentdidupdatefunction() {
const [count, setcount] = usestate(0);
const firstupdate = useref(true);
uselayouteffect(() => {
if (firstupdate.current) {
firstupdate.current = false;
return;
}
console.log("componentdidupdatefunction");
});
return (
<div>
<p>componentdidupdatefunction: {count} times</p>
<button
onclick={() => {
setcount(count + 1);
}}
>
click me
</button>
</div>
);
}
reactdom.render(
<componentdidupdatefunction />,
document.getelementbyid("app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
score:-1
if you want to skip the first render, you can create a state "firstrenderdone" and set it to true in the useeffect with empty dependecy list (that works like a didmount). then, in your other useeffect, you can check if the first render was already done before doing something.
const [firstrenderdone, setfirstrenderdone] = usestate(false);
//useeffect with empty dependecy list (that works like a componentdidmount)
useeffect(() => {
setfirstrenderdone(true);
}, []);
// your other useeffect (that works as componetdidupdate)
useeffect(() => {
if(firstrenderdone){
console.log("componentdidupdatefunction");
}
}, [firstrenderdone]);
score:-1
all previous are good, but this can be achieved in a simplier way considering that the action in useeffect can be "skipped" placing an if condition(or any other ) that is basically not run first time, and still with the dependency.
for example i had the case of :
- load data from an api but my title has to be "loading" till the date were not there, so i have an array, tours that is empty at beginning and show the text "showing"
- have a component rendered with different information from those api.
- the user can delete one by one those info, even all making the tour array empty again as the beginning but this time the api fetch is been already done
- once the tour list is empty by deleting then show another title.
so my "solution" was to create another usestate to create a boolean value that change only after the data fetch making another condition in useeffect true in order to run another function that also depend on the tour length.
useeffect(() => {
if (istitle) {
changetitle(newtitle)
}else{
issettitle(true)
}
}, [tours])
here my app.js
import react, { usestate, useeffect } from 'react'
import loading from './loading'
import tours from './tours'
const url = 'api url'
let newtours
function app() {
const [loading, setloading ] = usestate(true)
const [tours, settours] = usestate([])
const [istitle, issettitle] = usestate(false)
const [title, settitle] = usestate("our tours")
const newtitle = "tours are empty"
const removetours = (id) => {
newtours = tours.filter(tour => ( tour.id !== id))
return settours(newtours)
}
const changetitle = (title) =>{
if(tours.length === 0 && loading === false){
settitle(title)
}
}
const fetchtours = async () => {
setloading(true)
try {
const response = await fetch(url)
const tours = await response.json()
setloading(false)
settours(tours)
}catch(error) {
setloading(false)
console.log(error)
}
}
useeffect(()=>{
fetchtours()
},[])
useeffect(() => {
if (istitle) {
changetitle(newtitle)
}else{
issettitle(true)
}
}, [tours])
if(loading){
return (
<main>
<loading />
</main>
)
}else{
return (
<main>
<tours tours={tours} title={title} changetitle={changetitle}
removetours={removetours} />
</main>
)
}
}
export default app
score:0
simplified implementation
import { useref, useeffect } from 'react';
function mycomp(props) {
const firstrender = useref(true);
useeffect(() => {
if (firstrender.current) {
firstrender.current = false;
} else {
myprop = 'some val';
};
}, [props.myprop])
return (
<div>
...
</div>
)
}
score:0
i thought creating a custom hook would be overkill and i didn't want to muddle my component's readability by using the uselayouteffect
hook for something unrelated to layouts, so, in my case, i simply checked to see if the value of my stateful variable selecteditem
that triggers the useeffect
callback is its original value in order to determine if it's the initial render:
export default function mycomponent(props) {
const [selecteditem, setselecteditem] = usestate(null);
useeffect(() => {
if(!selecteditem) return; // if selected item is its initial value (null), don't continue
//... this will not happen on initial render
}, [selecteditem]);
// ...
}
score:1
@mehdidehghani, your solution work perfectly fine, one addition you have to do is on unmount, reset the didmount.current
value to false
. when to try to use this custom hook somewhere else, you don't get cache value.
import react, { useeffect, useref } from 'react';
const usedidmounteffect = (func, deps) => {
const didmount = useref(false);
useeffect(() => {
let unmount;
if (didmount.current) unmount = func();
else didmount.current = true;
return () => {
didmount.current = false;
unmount && unmount();
}
}, deps);
}
export default usedidmounteffect;
score:1
a simple way is to create a let
, out of your component and set in to true.
then say if its true set it to false then return (stop) the useeffect function
like that:
import { useeffect} from 'react';
//your let must be out of component to avoid re-evaluation
let isfirst = true
function app() {
useeffect(() => {
if(isfirst){
isfirst = false
return
}
//your code that don't want to execute at first time
},[])
return (
<div>
<p>its simple huh...</p>
</div>
);
}
its similar to @carmine tambasciabs solution but without using state :)
score:1
keep it simple:
function useeffectafterfirstrender(effect, deps) {
const isfirstrender = useref(true);
useeffect(() => {
if (isfirstrender.current) isfirstrender.current = false;
else return effect();
}, deps);
}
other solutions here reduce to this if you remove unnecessary complications:
- we need to pass the return value of
effect()
, because it might be a destructor, but we don't need to do any conditional logic to determine if it is or isn't. just pass it on, whatever it is, and letuseeffect
figure it out. - there's no point in resetting
isfirstrender
to true on unmount, because 1) the condition hasn't become true, and 2) on unmount, that ref is going in the incinerator. it doesn't get reused on the "next mount." there is no next mount. unmount is death.
here it is as a complete typescript module:
import { useeffect, useref, effectcallback, dependencylist } from 'react';
function useeffectafterfirstrender(effect: effectcallback, deps: dependencylist): void {
const isfirstrender = useref(true);
useeffect(() => {
if (isfirstrender.current) isfirstrender.current = false;
else return effect();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
}
export default useeffectafterfirstrender;
and i upvote kiran maniya's suggestion to give it an exhaustive-deps eslint rule:
{
"rules": {
"react-hooks/exhaustive-deps": ["warn", {
"additionalhooks": "useeffectafterfirstrender"
}]
}
}
score:2
this is the best implementation i've created so far using typescript
. basically, the idea is the same, using the ref
but i'm also considering the callback returned by useeffect
to perform cleanup on component unmount.
import {
useref,
effectcallback,
dependencylist,
useeffect
} from 'react';
/**
* @param effect
* @param dependencies
*
*/
export default function usenoinitialeffect(
effect: effectcallback,
dependencies?: dependencylist
) {
//preserving the true by default as initial render cycle
const initialrender = useref(true);
useeffect(() => {
let effectreturns: void | (() => void) = () => {};
// updating the ref to false on the first render, causing
// subsequent render to execute the effect
if (initialrender.current) {
initialrender.current = false;
} else {
effectreturns = effect();
}
// preserving and allowing the destructor returned by the effect
// to execute on component unmount and perform cleanup if
// required.
if (effectreturns && typeof effectreturns === 'function') {
return effectreturns;
}
return undefined;
}, dependencies);
}
you can simply use it, as usual as you use the useeffect
hook but this time, it won't run on the initial render. here is how you can use this hook.
useusenoinitialeffect(() => {
// perform something, returning callback is supported
}, [a, b]);
if you use eslint and want to use the react-hooks/exhaustive-deps rule for this custom hook:
{
"rules": {
// ...
"react-hooks/exhaustive-deps": ["warn", {
"additionalhooks": "usenoinitialeffect"
}]
}
}
score:8
@ravi, yours doesn't call the passed-in unmount function. here's a version that's a little more complete:
/**
* identical to react.useeffect, except that it never runs on mount. this is
* the equivalent of the componentdidupdate lifecycle function.
*
* @param {function:function} effect - a useeffect effect.
* @param {array} [dependencies] - useeffect dependency list.
*/
export const useeffectexceptonmount = (effect, dependencies) => {
const mounted = react.useref(false);
react.useeffect(() => {
if (mounted.current) {
const unmount = effect();
return () => unmount && unmount();
} else {
mounted.current = true;
}
}, dependencies);
// reset on unmount for the next mount.
react.useeffect(() => {
return () => mounted.current = false;
}, []);
};
score:9
same approach as tholle's answer, but using usestate
instead of useref
.
const [skipcount, setskipcount] = usestate(true);
...
useeffect(() => {
if (skipcount) setskipcount(false);
if (!skipcount) runyourfunction();
}, [dependencies])
edit
while this also works, it involves updating state which will cause your component to re-render. if all your component's useeffect
calls (and also all of its children's) have a dependency array, this doesn't matter. but keep in mind that any useeffect
without a dependency array (useeffect(() => {...})
will be run again.
using and updating useref
will not cause any re-renders.
score:56
i made a simple usefirstrender
hook to handle cases like focussing a form input:
import { useref, useeffect } from 'react';
export function usefirstrender() {
const firstrender = useref(true);
useeffect(() => {
firstrender.current = false;
}, []);
return firstrender.current;
}
it starts out as true
, then switches to false
in the useeffect
, which only runs once, and never again.
in your component, use it:
const firstrender = usefirstrender();
const phonenumberref = useref(null);
useeffect(() => {
if (firstrender || errors.phonenumber) {
phonenumberref.current.focus();
}
}, [firstrender, errors.phonenumber]);
for your case, you would just use if (!firstrender) { ...
.
score:133
you can turn it into custom hooks, like so:
import react, { useeffect, useref } from 'react';
const usedidmounteffect = (func, deps) => {
const didmount = useref(false);
useeffect(() => {
if (didmount.current) func();
else didmount.current = true;
}, deps);
}
export default usedidmounteffect;
usage example:
import react, { usestate, useeffect } from 'react';
import usedidmounteffect from '../path/to/usedidmounteffect';
const mycomponent = (props) => {
const [state, setstate] = usestate({
key: false
});
useeffect(() => {
// you know what is this, don't you?
}, []);
usedidmounteffect(() => {
// react please run me if 'key' changes, but not on initial render
}, [state.key]);
return (
<div>
...
</div>
);
}
// ...
Source: stackoverflow.com
Related Query
- Make React useEffect hook not run on initial render
- Make React useEffect only run at initial render and when property changes
- React hook useLayoutEffect not updating ref parameter on initial render
- React hook useEffect causes initial render every time a component mounts
- Using React hook to run Mobx method before initial render of component
- useEffect does not run in initial render
- How do I make UseEffect hook run step by step and not last?
- React useEffect Hook when only one of the effect's deps changes, but not the others
- Uncaught TypeError: create is not a function using useEffect React Hook with AJAX request
- React Testing Library does not find elements using getAllByTestId after initial render
- setTimeout not clearing with React useEffect hook on mobile devices
- React useEffect hook does not fire when prop dependency changes
- Replace of setState callback in react hook with useEffect hooks for complicated scenario not working
- React Hook does not work properly on the first render in gatsby production mode
- React setState hook not working with useEffect
- Assignments to the 'timeInterval' variable from inside React Hook useEffect will be lost after each render
- useEffect inside custom hook not getting called in correct oreder in React
- Make useEffect hook run before rendering the component
- useEffect hook called on initial render without dependency changing
- React component not re-rendering on URL parameter change when using useEffect hook to fetch data
- Elements not displaying with .map() in react functional component with useEffect hook
- Why does useEffect React Hook not work properly with dependency?
- Why does useEffect hook with its dependencies run after first component render
- How can I make useEffect not return the initial value? I keep getting empty array when I'm trying to pass an array of objects into a component
- React complains that hook is not used inside of a body function when my component is passed into another component to render
- React useRef combined with useEffect not rendering initial values
- Why React JS useEffect hook is not refreshing the text?
- React useEffect hook is not working with rails
- React Hook not setting with useEffect
- React Hooks Firebase - useEffect hook not returning any data
More Query from same tag
- Tag picker onChange event not firing
- React Redux how to get value in to my input
- Replace value in object
- How to render conditional HTML elements in React
- Tooltip on disabled Tab - Material Ui, React
- How can I delete an entire json object from an array of json objects and write it out to file?
- animate react component with transform and transition
- CORS Issue with React app and Laravel API
- How to make 301 redirect in NextJS Vercel project?
- How to fix findDOMNode warning from MaterialUI tooltip
- How to remove a nested array element in Redux Reducer?
- how load unsplash images in gatsby using gatsby-source-unsplash
- Async.js with Gulp turns error "Callback was already called"
- Import font into React application
- Using calc() in MUI
- toggle contentEditable on and off inside React function?
- Webpack production build: Unexpected token
- Is there any way to pass a variable from ReactJS to a CSS stylesheet?
- React - buggy issues with my simple radio button component placed on css grid
- React protected component's redux state is reset before thunk promise resolves
- ReactJS - add delete buttons to items displayed with map()
- How to show this blog django backend by its id in react frontend
- Reload page redirect to main page with react-router
- How can I execute terminal script/command in Google Compute Instance remotely from my React app?
- React - how to display data value in select menu
- What is the life cycle of a React component in context of Redux?
- How to delay page transition in NextJs
- Why can't I map my state+dispatch to this component? (TS error TS2741)
- POST request to Azure Functions App to be send twice before success in React (Axios, Fetch)
- How to set state and use it in a function using react and typescript?