Dariusz Legizynski
Dariusz Legizynski

Reputation: 384

Redux: Why does my useEffect() keeps rerendering its value on every page rerender

I am learning react-redux. I got the following problem:

  1. I make two async api calls (with redux-thunk):
  1. Those country names I use afterwards to make a second api call, to get all the soccer leagues, that are in those countrys (sometimes, there are none, so I get a null). In this case, the call is made with each countryName separatly. I make out of the results an array.
  2. This arrays length is 255m out of which I filter out the null values and map the leagues names.
  3. After I click on a League's name, a page is rendered ({Link} from "react-router-dom";). NOW my problem occurs
  4. When I click, to get back to my home page (<Link to={"/"} >), both useEffect() are making an api call again. Why?

Here is the code for my useEffect():

const dispatch = useDispatch();
const selectAllCountries = useSelector(state => state.allCountries);
const selectAllLeagues = useSelector(state => state.allLeagues);

useEffect(() => {
    dispatch(allCountries());
}, [dispatch]);

useEffect(() => {
    if(!_.isEmpty(selectAllCountries.data)) {
        selectAllCountries.data.countries.map(el => dispatch(allLeagues(el.name_en)));
    }
}, [dispatch, selectAllCountries.data]);

I tried to make a custom hook and put the useEffect() in there:

const useCountries = getCountries => {useEffect(() => {
dispatch(getCountries());
},[getCountries])}

useCountries(allCountries);

as suggested here: React hooks: dispatch action from useEffect

But it didnt help.

Will be greatful for any help.


ANSWER:

in "./actions/.../allLeagues.js

...
import _ from "lodash";

export const allLeagues = (country) => async (dispatch, getState) => {

    if (!_.isEmpty(getState().allLeagues) && !_.isEmpty(getState().allLeagues.data)) {
        return Promise.resolve();
    } else {
        try {

          ...
        
        }
    }    
}

Question, that was also helpfull: Fetching data from store if exists or call API otherwise in React (take look at answer about getStore())

Upvotes: 0

Views: 554

Answers (1)

ericgio
ericgio

Reputation: 3499

As mentioned in a comment above, the homepage unmounts when you click to go to a new page. When you go back, the page re-mounts and the effect runs again, triggering another API call. You can prevent the API call by checking whether or not the values already exist in your store. I personally like to do this in the action creator, but you could do it in the effect as well.

Checking state in the action creator:

function allLeagues(countryName) {
  return (dispatch, getState) => {
    // Call `getState` and check whether `allLeagues` has been populated yet.
    const { allLeagues } = getState();

    if (allLeagues && allLeagues.data && allLeagues.data.length) {
      // You already have the data, no need to make the API call.
      return Promise.resolve();
    }

    // No data, make the API call...
  };
}

Checking state in the effect:

useEffect(() => {
  // Check whether the league data is set or not.
  if(!_.isEmpty(selectAllCountries.data) && _.isEmpty(selectAllLeagues.data)) {
    selectAllCountries.data.countries.map(el => dispatch(allLeagues(el.name_en)));
  }
}, [dispatch, selectAllCountries.data, selectAllLeagues.data]);

Upvotes: 2

Related Questions