Search code examples
reactjsuse-effect

prevent api from being called before state is updated


I have a list of objects. I want to make an api call once the location field of the object is changed. So for that, I have a useEffect that has id, index and location as its dependencies. I have set a null check for the location, if the location isn't empty, I want to make the api call. But the thing is, the api is being called even when the location is empty, and I end up getting a 400. How can I fix this and make the call once location isn't empty?

    const [plants, setPlants] = useState([
        {
            name: 'Plant 1',
            id: uuidv4(),
            location: '',
            coords: {},
            country: '',
            isOffshore: false,
        }
    ]);
    const [locationIDObject, setlocationIDObject] = useState({
        id: plants[0].id,
        index: 0
    });

    const handlePlantLocChange = (id, index, value) => {
        setPlants(
            plants.map(plant => 
                plant.id === id
                ? {...plant, location: value}
                : plant
            )
        )
        setlocationIDObject({
            id: id,
            index: index
        })
    }

    const getCoords = (id, index) => {
        axios.get('http://localhost:3002/api/coords', {
            params: {
                locAddress: plants[index].location
            }
        }).then((response) => {
            if(response.status === 200) {
                handlePlantInfoChange(id, PlantInfo.COORD, response.data)
            }
        })
    }

    const handler = useCallback(debounce(getCoords, 5000), []);

    useDeepCompareEffect(() => {
        if(plants[locationIDObject.index].location !== '')
            handler(locationIDObject.id, locationIDObject.index);
    }, [ locationIDObject, plants[locationIDObject.index].location])

    return (
        <div className='plant-inps-wrapper'>
            {
                plants.map((item, idx) => {
                    return (
                        <div key={item.id} className="org-input-wrapper">
                            <input placeholder={`${item.isOffshore ? 'Offshore' : 'Plant ' + (idx+1) + ' location'}`} onChange={(e) => handlePlantLocChange(item.id, idx, e.target.value)} value={item.isOffshore ? 'Offshore' : item.location} className="org-input smaller-input"/>
                        </div>
                    )
                })
            }
        </div>
    )

Solution

  • So it turns out the issue was with my debouncing function. I don't know what exactly the issue was, but everything worked as expected when I replaced the debouncing function with this:

        useEffect(() => {
            console.log("it changing")
            const delayDebounceFn = setTimeout(() => {
                getCoords(locationIDObject.id, locationIDObject.index)
            }, 4000)
    
            return () => clearTimeout(delayDebounceFn)
        },[...plants.map(item => item.location), locationIDObject.id, locationIDObject.index])