Thales Maia
Thales Maia

Reputation: 189

For what reason a dispatch in React-Redux is being calling first than another

I build a Pokedex and works fine, but, I see a "problem" in React-Redux Dev Tools:

React-Redux Dev Tools print

"getPokemonsInfo" is being calling before the "getPokemonsUrls", but, "getPokemonsInfo" only should be calling when a property of a state changed (using useEffect()), and this happens with "getPokemonsUrls".

import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";

import Home from "./Components/Home";

import { getPokemonsUrls } from "./helpers/helpers";

const App = () => {
  const urlDefault = useSelector(({ urlDefault }) => urlDefault);
  const dispatch = useDispatch();

  useEffect(async() => {
    dispatch(await getPokemonsUrls(urlDefault));
  }, []);

  return <Home></Home>
};

export default App;
import React from "react";
import { useSelector, useDispatch } from "react-redux";

import PokemonsContainer from "./PokemonsContainer";

import { getPokemonsUrls } from '../helpers/helpers';

const Home = () => {
  const urlDefault = useSelector(({ urlDefault }) => urlDefault);
  const dispatch = useDispatch();
  
  return(
    <>
      <h1>Pokedex</h1>
      <button onClick={async() => dispatch(await getPokemonsUrls(urlDefault))}>
        Get More Pokemons
      </button>
      <PokemonsContainer></PokemonsContainer>
    </>
  )
};

export default Home;
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';

import { getPokemonsInfo } from "../helpers/helpers";

const PokemonsContainer = () => {
  const pokemonsUrls = useSelector(({ pokemonsUrls }) => pokemonsUrls);
  const pokemonsInfo = useSelector(({ pokemonsInfo }) => pokemonsInfo);

  const dispatch = useDispatch();

  useEffect(async() => {
    dispatch(await getPokemonsInfo(pokemonsUrls));
  }, [pokemonsUrls]);

  return (
    pokemonsInfo.map(({ id, name, image }) => (
      <div key={id}>
        <p>{name}</p>
        <img src={image}/>
      </div>
    ))
  )    
  
};

export default PokemonsContainer;
import axios from "axios";

const getPokemonsUrls = async(url) => {
  const response = await axios.get(url);
  const data = await response.data;

  return {
    type: "getPokemonsUrls",
    payload: {
      urlDefault: data.next? data.next: null,
      pokemonsUrls: data.results.map(result => result.url)
    }
  }
};

const getPokemonsInfo = async(pokemonsUrls) => {
  const response = await axios.all(pokemonsUrls.map(url => axios.get(url)));
  const data = response.map(res => res.data);

  return {
    type: "getPokemonsInfo",
    payload: filterPokemonsInfo(data)
  }
};

const filterPokemonsInfo = data=> {
  return data.map(({ name, id, sprites, types, moves}) => ({
    name,
    id,
    image: sprites.front_default,
    types,
    moves
  }))
};

export { getPokemonsUrls, getPokemonsInfo };
import { createStore } from 'redux';

const initialState = {
  urlDefault: "https://pokeapi.co/api/v2/pokemon/?offset=0&limit=20",
  pokemonsUrls: [],
  pokemonsInfo: [],
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "getPokemonsUrls":
      return{
        ...state,
        urlDefault: action.payload.urlDefault,
        pokemonsUrls: action.payload.pokemonsUrls
      };
    case "getPokemonsInfo":
      return {
        ...state,
        pokemonsInfo: state.pokemonsInfo.concat(action.payload)
      }
    default:
      return state;
  }
};

const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())

export default store;

For why reason this is happening?

Upvotes: 1

Views: 68

Answers (1)

Chris
Chris

Reputation: 118

This is expected behavior in React. The useEffect hook will run when the component mounts, even if you have a dependency listed. A component is not considered to be mounted until after its children have mounted. Therefore the useEffect hook in the child component will execute before the useEffect hook in the parent.

You can add a condition inside the useEffect hook that fetches the Pokémon info. That condition can check to see if the URL’s have been loaded and only run the getPokemonInfo function if so. That way the function inside the useEffect will only execute after the urls have been fetched

Upvotes: 1

Related Questions