Lokesh Bajracharya
Lokesh Bajracharya

Reputation: 540

useEffect only if state changed

I have useEffect which calls an action from redux to fetch uploads

useEffect(() => {
    getUploads()
}, [getUploads])

However, I only want to fetch when the state changes, not fetch everytime the component renders. I have mapped the state like:

{filteredUploads.map((image, i) => { return (...) })}

I have tried to add getUploads, filteredUploads, filteredUploads.length as dependency array. None of it worked.

My redux-action:

export const getUploads = () => async dispatch => {
    try {
        dispatch({ type: 'LOADING', payload: true })

        const res = await axios.get('/uploads/myuploads')
        dispatch({
            type: GETMYUPLOAD_SUCCESS,
            payload: res.data
        })

    } catch (err) {
        const error = err.response.data.error[0].msg

        dispatch(setAlert(error, 'danger'))
    }
}

mapStatetoProps:

function mapStateToProps(state) {
    const { myuploads, searchField } = state.useruploads;
    return {

        searchField: state.useruploads.searchField,

        filteredUploads: myuploads.filter((upload) => upload.caption.toLowerCase().includes(searchField.toLowerCase()))
    };
}

Upvotes: 11

Views: 39369

Answers (1)

Henry Woody
Henry Woody

Reputation: 15652

To have the useEffect hook called when state updates, you'll need to include the relevant state variables in the dependency array of useEffect (the second argument passed to useEffect), which it seems you've attempted.

To address useEffect being called on every render of the component: this is happening because a value in the dependency array (getUploads) is changing, or being redefined, on every render. To fix this, you can use the useDispatch redux hook instead of mapDispatchToProps. The useDispatch hook provides a stable reference (meaning it won't change and cause useEffect to run) to the dispatch function from the Redux store.

Here's a full example:

import { useDispatch } from "react-redux";
import { getUploads } from "./redux-actions";


const MyComponent = props => {
    const dispatch = useDispatch();
    
    const [state, setState] = React.useState({});
    
    useEffect(() => {
        dispatch(getUploads());
    }, [dispatch, state]);

    // rest of component
}

Upvotes: 14

Related Questions