Home > other >  How to stop initial render for useEffect hook
How to stop initial render for useEffect hook

Time:10-15

Earlier I had a Class component, so I didn't face any issues while using lifecycle methods, but after converting to useEffect hooks, I am facing the initial render issue which I don't want to happen.

Class

componentDidMount() {
    this.setState({
        patchVal:this.props.patchTaskVal,
        startTime:this.props.patchStartTime,
        setEndTime:this.props.patchEndTime
    })
}
    
componentDidUpdate(prevProps) {
    if (prevProps.patchTaskVal !== this.props.patchTaskVal) {
        this.callValidation()
    }

    if (prevProps.closeTask !== this.props.closeTask) {
        this.setState({
            showValue:false,
            progressValue:[],
            startTime:new Date(),
            setEndTime:""
        })
    }

    if (prevProps.patchStartTime !== this.props.patchStartTime || prevProps.endTime !== this.props.endTime && this.props.endTime !== "") {
        this.setState({
            startTime:this.props.patchStartTime,
            setEndTime:parseInt(this.props.endTime)
        })
    }
}

Functional

const [patchTaskVal, setPatchTaskVal]=useState(/*initial value */) 
const [startTime, setStartTime]=useState()
const [endTime, setEndTime] = useState()
    
**// I want only this useEffect to run on the initial render**
useEffect(() => {
    setPatchTaskVal(props.patchTaskVal)
    ...//set other states
}, [])
    
useEffect(() => {
    callValidation() 
}, [props.patchTaskVal])
    
useEffect(() => {
    //setShowValue...
}, [props.closeTask])
    
useEffect(() => {
    if (props.endTime != "") {
        // set states...
    }
}, [props.patchStartTime,props.endTime])

Here I am facing an issue where all the useEffects are running on the initial render, Please suggest a solution for this so that only the first useEffect will run on the initial render and all other useEffects will run according to its dependency prop values.

CodePudding user response:

You basically need a ref which will tell you whether this is the first render on not. Refs values persist over rerenders. You can start with a truthy value and toggle it to false after the first render (using a useEffect with an empty array[]). Based on that you can run your desired code.

You can also put the whole thing in a custom hook:

import { useEffect, useRef } from "react";

const useOnUpdate = (callback, deps) => {
  const isFirst = useRef(true);
  useEffect(() => {
    if (!isFirst.current) {
      callback();
    }
  }, deps);

  useEffect(() => {
    isFirst.current = false;
  }, []);
};

export default useOnUpdate;

You can call this hook in your component like :

  useOnUpdate(() => {
    console.log(prop);
  }, [prop]);

In the hook: After the initial render, both useEffects run. But when the first effect runs the value of the isFirst.current is true. So the callback is not called. The second useEffect also runs and sets isFirst.current to false. Now in subsequent renders only the first useEffect run (when dependencies change), and isFirst.current is false now so callback is executed.

The order of the two useEffects is very important here. Otherwise, in the useEffect with deps, isFirst.current will be true even after the first render.

Link

CodePudding user response:

try this...

let init = true;
useEffect( ()=>{
if(init) {

    setPatchTaskVal(props.patchTaskVal)
init = false;
    ...//set other states}
}, [])

useEffect( ()=> {

    !init && callValidation() 
},[props.patchTaskVal])

useEffect( ()=>{
//!init && setShowValue...
},[props.closeTask])

useEffect( ()=>{
    if(props.endTime!="" && !init){
    // set states...
    }
},[props.patchStartTime,props.endTime])

CodePudding user response:

Hope my understanding is right about your question.
Why not just add a if statement to check the state is not undefined or default value

useEffect( ()=> {
  if (props.patchTaskVal) {
    callValidation() 
  }
},[props.patchTaskVal])

useEffect( ()=>{
  if (props.closeTask) {
    //setShowValue...  
  }
},[props.closeTask])

useEffect( ()=>{
    if(props.patchStartTime){
    // set states...
    }
    
    if(props.endTime){
    // set states...
    }
},[props.patchStartTime,props.endTime]

And according your class component,

this.setState({
patchVal:this.props.patchTaskVal,
startTime:this.props.patchStartTime,
setEndTime:this.props.patchEndTime
})

The function component should map props to component's state. Like this

const [patchTaskVal, setPatchTaskVal]=useState(props.patchTaskVal) 
const [startTime, setStartTime]=useState(props.patchStartTime)
const [endTime, setEndTime] = useState(props.patchEndTime)
  • Related