Search code examples
javascriptreactjsuse-effectuse-state

How can i prevent useEffect() from firing up the first time but listening for a state change?


I'm using react, node express, postgres

I have a react component that is an html table that gets populated from a postgres table.

Here is parent component Materials:

const Materials = () => {
    const [thickness1, setThickness] = useState(0);
    const [width1, setWidth] = useState(0);
    const [length1, setLength] = useState(0);
    const [partTotalDemand, setTotalDemand] = useState(0);
    const [partPlanned, setPlanned] = useState(0);
...

Here is a method in the component that retrieves data

// Material requirements calculation
    const getReq = async (id) => {
        try {
            const response = await fetch(`http://localhost:5000/materials/${id}`, [id])
            const jsonData = await response.json();

            const tempThickness = jsonData.parts_material_thickness
            const tempWidth = jsonData.parts_material_width
            const tempLength = jsonData.parts_material_length
            const tempTotalDemand = jsonData.workorder_total
            const tempPlanned = jsonData.parts_produced

            stateSetter(tempThickness, tempWidth, tempLength)
        } catch (err) {
            console.log(err.message);
        }
    }

I then want to update the states of the global constants:

const stateSetter = (thickness, width, length) => {
        try {
            setThickness(thickness);
            setWidth(width);
            setLength(length);

            console.log(thickness1);
            console.log(width1);
            console.log(length1);

        } catch (err) {
            console.log(err.message)
        }
    }

    useEffect(() => {
        stateSetter();
    }, [thickness1]);

Essentially the getReq() method is supposed to retrieve the information, and then I need to update the states with those values. As I understand I then need to re-render the component so the new states are usable. I attempted to do this via useEffect() but I'm not successful. The idea was to stop getReq() from firing up on the first render, but if the state changes for thickness1/width1/length1 then it should fire up and re-render, help much appreciated!


Solution

  • You're over-complicating this. All you need to do is set the state values:

    const getReq = async (id) => {
      try {
        const response = await fetch(`http://localhost:5000/materials/${id}`, [id])
        const jsonData = await response.json();
    
        // set state values
        setThickness(jsonData.parts_material_thickness);
        setWidth(jsonData.parts_material_width);
        setLength(jsonData.parts_material_length);
        setTotalDemand(jsonData.workorder_total);
        setPlanned(jsonData.parts_produced);
      } catch (err) {
        console.log(err.message);
      }
    }
    

    You don't need to manually do anything to re-render the component. It will re-render whenever state is updated. So the "setter" functions being invoked here will trigger that re-render. (All of the state updates will be batched. So the above won't trigger 5 re-renders, just one with the 5 updated state values.)

    Where you would use useEffect is when you want to have some logic which responds to a change in a particular state. For example, if you want to show a message every time thickness changes to a negative value, you'd do something like:

    useEffect(() => {
      if (thickness < 1) {
        alert('negative thickness!');
      }
    }, [thickness]);
    

    But that's not what you're doing here. All you're doing here is setting state values.