Search code examples
javascriptreactjsreduxredux-toolkit

How to call an object using useSelector hook in react


I am currently trying to get the array of objects namely -Animearray in another component and i am getting an error of undefined in the console. Here is the code of the Store component

import {
  configureStore,
  createAsyncThunk,
  createSlice,
} from "@reduxjs/toolkit";
import { API_KEY, TMBD_BASE_URL } from "../utils/constent";
import axios from "axios";
const initialState = {
  movies: [],
  genresLoaded: false,
  genres: [],
};
const initialAnime = {
  anime: [],
  genresLoaded: false,
  genres: [],
};

const createArrayfromRawdata = (array, moviesArray, genres) => {
  array.forEach((movie) => {
    const movieGenres = [];
    movie.genre_ids.forEach((genre) => {
      const name = genres.find(({ id }) => id === genre);
      if (name) movieGenres.push(name.name);
    });
    if (movie.backdrop_path)
      moviesArray.push({
        id: movie.id,
        name: movie?.original_name ? movie.original_name : movie.original_title,
        image: movie.backdrop_path,
        genres: movieGenres.slice(0, 3),
      });
  });
};
async function createAnimeFromRawData(rawData, animeArray) {
  const data = rawData;
  console.log(animeArray);
  for (let i = 0; i < data.length; i++) {
    const anime = data[i];
    if (anime) {
      const genreArr = anime.genres.map((genre) => genre.name);
      animeArray.push({
        name: anime.title,
        genre: genreArr,
        score: anime.score,
        image: anime.images.jpg.image_url,
        trailer: anime.trailer.embed_url,
        episodes: anime.episodes,
        synopsis: anime.synopsis,
      });
    }
  }
  console.log(animeArray);
  return animeArray;
}

export const RawdataAnime = async () => {
  const Animearray = [];
  for (let i = 1; Animearray.length < 60 && i < 10; i++) {
    const { data } = await axios.get(`https://api.jikan.moe/v4/top/anime`); // Equivalent to response.data
    const results = data?.data || [];

    try {
      await createAnimeFromRawData(results, Animearray);
      await new Promise((resolve) => setTimeout(resolve, 1000));
    } catch (error) {
      console.error(error);
    }
  }
  return Animearray;
};

const rawData = async (api, genres, paging) => {
  const moviesArray = [];
  for (let i = 1; moviesArray.length < 60 && i < 10; i++) {
    const {
      data: { results },
    } = await axios.get(`${api}${paging ? `&page=${i}` : ""}`);
    createArrayfromRawdata(results, moviesArray, genres);
  }
  return moviesArray;
};
export const fetchMovies = createAsyncThunk(
  "neflix/trending",
  async ({ type }, thunkAPI) => {
    const {
      netflix: { genres },
    } = thunkAPI.getState();
    return rawData(
      `${TMBD_BASE_URL}/trending/${type}/week?api_key=${API_KEY}`,
      genres,
      true
    );
  }
);

//`${TMBD_BASE_URL}/discover/${type}?api_key=${API_KEY}&with_genres=${genres}`
export const getGenres = createAsyncThunk("netflix/genres", async () => {
  const {
    data: { genres },
  } = await axios.get(`${TMBD_BASE_URL}/genre/movie/list?api_key=${API_KEY}`);

  return genres;
});

const netflixSlice = createSlice({
  name: "netflix",
  initialState,
  extraReducers: (builder) => {
    builder.addCase(getGenres.fulfilled, (state, action) => {
      state.genres = action.payload;
      state.genresLoaded = true;
    });
    builder.addCase(fetchMovies.fulfilled, (state, action) => {
      state.movies = action.payload;
    });
  },
});

const animeSlice = createSlice({
  name: "anime",
  initialState: initialAnime,
  extraReducers: (builder) => {
    builder.addCase(RawdataAnime.fulfilled, (state, action) => {
      state.anime = action.payload;
    });
  },
});
export const store = configureStore({
  reducer: {
    netflix: netflixSlice.reducer,
    anime: animeSlice.reducer,
  },
});

and turns out when I tried to use the animeArray in my main component it did not load

import BackgroundVid from "../components/BackgroundVid";
import { fetchMovies, getGenres, RawdataAnime, setAnime } from "../store";

import Slider from "../components/Slider";

export default function Netflix() {
  const [scrolled, isScrolled] = useState(false);
  const genresLoaded = useSelector((state) => state.netflix.genresLoaded);
  const movies = useSelector((state) => state.netflix.movies);
  const anime = useSelector((state) => state.anime.anime);
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(getGenres());
    onAuthStateChanged(firbaseauth, (user) => {
      if (user) {
        setUser(user);
      } else {
        setUser(null);
      }
    });
  }, []);
  useEffect(() => {
    if (genresLoaded) {
      dispatch(fetchMovies({ type: "all" }));
    }
  });

If you have any suggestions on how can i get the data from that component do let me know. The targeted data is stored in the component as animeArray


Solution

  • From what I can tell, the RawdataAnime function isn't an action creator. If you want to have a RawdataAnime.fulfilled reducer case then it should be converted to an asynchronous action so when the returned Promise resolves, i.e. it is fulfilled, the reducer can handle the returned payload.

    Example: store

    import {
      configureStore,
      createAsyncThunk,
      createSlice,
    } from "@reduxjs/toolkit";
    import axios from "axios";
    
    const initialAnime = {
      anime: [],
      genresLoaded: false,
      genres: [],
    };
    
    ...
    
    function createAnimeFromRawData(rawData) {
      return rawData.map((anime) => ({
        id: anime.mal_id,
        name: anime.title,
        genre: anime.genres.map((genre) => genre.name),
        score: anime.score,
        image: anime.images.jpg.image_url,
        trailer: anime.trailer.embed_url,
        episodes: anime.episodes,
        synopsis: anime.synopsis
      }));
    }
    
    export const RawdataAnime = createAsyncThunk("anime/fetchAnime", async () => {
      const { data } = await axios.get(`https://api.jikan.moe/v4/top/anime`); // Equivalent to response.data
      const results = data?.data || [];
      return createAnimeFromRawData(results);
    });
    
    ...
    
    const animeSlice = createSlice({
      name: "anime",
      initialState: initialAnime,
      extraReducers: (builder) => {
        builder.addCase(RawdataAnime.fulfilled, (state, action) => {
          state.anime = action.payload;
        });
      }
    });
    
    export const store = configureStore({
      reducer: {
        netflix: netflixSlice.reducer,
        anime: animeSlice.reducer
      }
    });
    

    App

    const dispatch = useDispatch();
    const anime = useSelector((state) => state.anime.anime);
    
    useEffect(() => {
      dispatch(RawdataAnime());
    }, []);