How to call an object using useSelector hook in react - javascript

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

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());
}, []);

Related

I am getting undefined while sending patch request

I am working on a MERN app with redux toolkit. Currently, I am facing a problem with my update functionality, when I click on the update button I can see in redux dev tools the request is rejected and in the console, the id is showing undefined while I am passing it. I am probably missing something in my code, if someone can point it out and explain that would be great. Thanks in advance. Here below are my code:
postService.js:
import axios from 'axios';
const API_URL = '/api/posts/';
const updatePost = async (_id, postData) => {
const response = await axios.patch(API_URL + _id, postData);
return response.data;
};
const postService = {
updatePost,
};
export default postService;
postSlice.js:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
import postService from './postService';
const initialState = {
posts: [],
isError: false,
isSuccess: false,
isLoading: false,
message: '',
};
export const updatePost = createAsyncThunk(
'posts/updatePost',
async ({ id, postData }, thunkAPI) => {
const { postCreator, title, body, imageFile } = postData;
try {
return await postService.updatePost(id, {
postCreator,
title,
body,
imageFile,
});
} catch (error) {
console.log(error.message);
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
export const postSlice = createSlice({
name: 'post',
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
.addCase(updatePost.pending, (state) => {
state.isLoading = true;
})
.addCase(updatePost.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
const { id, postCreator, title, body, imageFile } = action.payload;
const existingPost = state.find((post) => post.id === id);
if (existingPost) {
existingPost.postCreator = postCreator;
existingPost.title = title;
existingPost.body = body;
existingPost.imageFile = imageFile;
}
})
.addCase(updatePost.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
export default postSlice.reducer;
Form.js:
const Form = ({ activeId, setActiveId }) => {
const [postData, setPostData] = useState({
postCreator: '',
title: '',
body: '',
imageFile: '',
});
const post = useSelector((state) =>
activeId ? state.posts.posts.find((post) => post._id === activeId) : null
);
const user = JSON.parse(localStorage.getItem('user'));
const dispatch = useDispatch();
useEffect(() => {
if (post) setPostData(post);
}, [post]);
const clearInputField = () => {
setActiveId(0);
setPostData({
postCreator: '',
title: '',
body: '',
imageFile: '',
});
};
const handleSubmit = async (e) => {
e.preventDefault();
if (activeId) {
dispatch(updatePost({ activeId, postData }));
clearInputField();
} else {
dispatch(createPost(postData));
clearInputField();
}
};
In the updatePost thunk in postSlice.js, you are attempting to destructure the variables { id, postData } from the payload creator args.
But in Form.js, you are sending an object { activeId, postData } when you dispatch updatePost.
So both id and postData will be undefined because neither exist on the object.
You could change it to:
dispatch(updatePost({id: activeId, postData: formData}))

error when passing parameters to an async redux action

I am trying to create a scraping application using redux toolkit for learning purposes but the scraping process fails whenever I pass custom parameters in the dispatch statement but works correctly on passing default parameters in the thunk
My async thunk
export const loadData = createAsyncThunk(
"alldata/getdata",
async ({ pageNo, language }, thunkAPI) => {
const data = await fetch(
`http://localhost:5000/scrape?pageNo=${encodeURIComponent(
pageNo
)}&language=${encodeURIComponent(language)}`
);
const json = data.json();
return json;
}
);
My slice
const projectSlice = createSlice({
name: "allprojects",
state: {
projectState: [],
workingState: [],
isLoading: false,
hasError: false,
},
reducers: {
addProject: (state, action) => {
return state.workingState.push(action.payload);
},
removeProject: (state, action) => {
return state.workingState.filter(
(project) => project.id !== action.payload.id
);
},
},
extraReducers: {
[loadData.pending]: (state, action) => {
state.isLoading = true;
state.hasError = false;
},
[loadData.fulfilled]: (state, action) => {
const { json } = action.payload;
state.isLoading = false;
state.hasError = false;
},
[loadData.rejected]: (state, action) => {
state.isLoading = false;
state.hasError = true;
},
},
});
export const { addProject, removeProject } = projectSlice.actions;
export const { Projectreducer } = projectSlice.reducer;
export const selectAllPosts = (state) => state.allprojects.projectState;
Calling the async action
React.useEffect(() => {
console.log(dispatch(loadData(1, "ruby")));
}, []);
//url:https://github.com/search?p=undefined&q=language%3Aundefined
how do I solve this error
The async thunk arguments must be collected in one object:
dispatch(loadData({ pageNo: 1, language: "ruby" }))
See https://redux-toolkit.js.org/api/createAsyncThunk#payloadcreator

Uncaught ReferenceError: Cannot access createAsyncThunk before initialization within a redux toolkit slice

I'm currently struggling to solve this error which i believe might be a circular reference dependency issue within my react redux application. It was working fine at first but then all of a sudden my app is throwing this error:
From the error, it says checkAuth cannot be accessed before initialisation. checkAuth is a createAsyncThunk that is exported from authentication slice and is imported into the application slice to be added as extra reducers. Right now I am very confused on how to solve this issue. Hopefully you guys could help me out.
Here are the codes:
store.js
import { configureStore } from "#reduxjs/toolkit";
import authReducer from "./authentication/authenticationSlice";
import appReducer from "./application/appSlice";
import errorReducer from "./error/errorSlice";
const store = configureStore({
reducer: {
auth: authReducer,
error: errorReducer,
app: appReducer,
},
});
export default store;
authenticationSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import { axiosPublic, axiosPrivate } from "../../utils/axios";
const initialState = {
isAuthLoading: false,
authUser: null,
};
const checkAuth = createAsyncThunk("auth/checkAuth", async (_, thunkAPI) => {
try {
const response = await axiosPrivate.get("/auth");
return response.data;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
});
const login = createAsyncThunk("auth/login", async (credentials, thunkAPI) => {
try {
const response = await axiosPublic.post("/auth/login", credentials, {
withCredentials: true,
});
return response.data;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
});
const logout = createAsyncThunk("auth/logout", async (_, thunkAPI) => {
try {
await axiosPrivate.post("/auth/logout", null, { withCredentials: true });
return;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
});
const reqOTPHandler = createAsyncThunk(
"auth/reqOTPHandler",
async ({ endpoint, credentials }, thunkAPI) => {
try {
await axiosPublic.post(endpoint, credentials);
return;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
}
);
const signup = createAsyncThunk(
"auth/submitOTP",
async (credentials, thunkAPI) => {
try {
const response = await axiosPublic.post(
"/auth/confirm-signup",
credentials,
{
withCredentials: true,
}
);
return response.data;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
}
);
const reqResetHandler = createAsyncThunk(
"/auth/reqResetHandler",
async ({ endpoint, token }, thunkAPI) => {
try {
await axiosPublic.post(endpoint, {
token,
});
return;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
}
);
const resetPassword = createAsyncThunk(
"auth/resetPassword",
async ({ endpoint, credentials }, thunkAPI) => {
try {
await axiosPublic.post(endpoint, credentials);
return;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
}
);
const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
loading: (state) => {
state.isAuthLoading = true;
},
idle: (state) => {
state.isAuthLoading = false;
},
updateAuthUser: (state, action) => {
state.authUser = action.payload;
},
removeAuthUser: (state) => {
state.authUser = null;
},
},
extraReducers: {
[checkAuth.fulfilled]: (state, action) => {
state.authUser = action.payload;
},
[login.pending]: (state) => {
state.isAuthLoading = true;
},
[login.fulfilled]: (state, action) => {
state.isAuthLoading = false;
state.authUser = action.payload;
},
[login.rejected]: (state, action) => {
state.isAuthLoading = false;
},
[logout.fulfilled]: (state, action) => {
state.authUser = null;
},
[reqOTPHandler.pending]: (state) => {
state.isAuthLoading = true;
},
[reqOTPHandler.fulfilled]: (state) => {
state.isAuthLoading = false;
},
[reqOTPHandler.rejected]: (state) => {
state.isAuthLoading = false;
},
[signup.pending]: (state) => {
state.isAuthLoading = true;
},
[signup.fulfilled]: (state, action) => {
state.isAuthLoading = false;
state.authUser = action.payload;
},
[signup.rejected]: (state) => {
state.isAuthLoading = false;
},
[reqResetHandler.pending]: (state) => {
state.isAuthLoading = true;
},
[reqResetHandler.fulfilled]: (state, action) => {
state.isAuthLoading = false;
},
[reqResetHandler.rejected]: (state) => {
state.isAuthLoading = false;
},
[resetPassword.pending]: (state) => {
state.isAuthLoading = true;
},
[resetPassword.fulfilled]: (state, action) => {
state.isAuthLoading = false;
},
[resetPassword.rejected]: (state) => {
state.isAuthLoading = false;
},
},
});
const { reducer } = authSlice;
const { loading, idle, updateAuthUser, removeAuthUser } = authSlice.actions;
export {
loading as authLoading,
idle as authIdle,
updateAuthUser,
removeAuthUser,
// thunks
checkAuth,
login,
logout,
reqOTPHandler,
signup,
reqResetHandler,
resetPassword,
};
export default reducer;
appSlice.js
import { createSlice } from "#reduxjs/toolkit";
import { checkAuth, logout } from "../authentication/authenticationSlice";
const initialState = {
// Global App loading state
isInitializing: true,
isAppLoading: false,
currentBranch: null,
currentRole: null,
};
const appSlice = createSlice({
name: "app",
initialState,
reducers: {
appInitializing: (state) => {
state.isInitializing = true;
},
appInitialized: (state) => {
state.isInitializing = false;
},
appLoading: (state) => {
state.isAppLoading = true;
},
appFullfilled: (state) => {
state.isAppLoading = false;
},
},
extraReducers: {
// auth
[checkAuth.pending]: (state) => {
state.isInitializing = true;
},
[checkAuth.fulfilled]: (state) => {
state.isInitializing = false;
},
[checkAuth.rejected]: (state) => {
state.isInitializing = false;
},
[logout.pending]: (state) => {
state.isAppLoading = true;
},
[logout.fulfilled]: (state) => {
state.isAppLoading = false;
},
[logout.rejected]: (state) => {
state.isAppLoading = false;
},
},
});
const { reducer } = appSlice;
export const { appInitializing, appInitialized, appLoading, appFullfilled } =
appSlice.actions;
export default reducer;
Don't use the extraReducers object notation - that one will be deprecated soon as it doesn't work with TypeScript - and has this exact problem. If you use the builder notation instead, that will work.
extraReducers: builder => {
// auth
builder.addCase(checkAuth.pending. (state) => {
state.isInitializing = true;
});
...
}

react/redux fetching data from redux slice issue

I am creating a react/ redux app using json fake api server redux toolkit.
I am adding a some datas from and api to redux slice and trying to retrieve to my component. data seems empty and no error showing could anyone able to help me in this if possible.
my redux slice
import {
createAsyncThunk,
createSlice,
createSelector,
} from "#reduxjs/toolkit";
import axios from "axios";
import { base_emp } from "./api";
const initialState = {
emplist: [],
loading: "loading",
};
export const fetchEmployees = createAsyncThunk(
"employee/emplist",
async (_, thunkAPI) => {
try {
const response = await axios.get(base_emp);
return await response.json();
} catch (error) {
return thunkAPI.rejectWithValue({ error: error.message });
}
}
);
export const userSlice = createSlice({
name: "user",
initialState,
// The `reducers` field lets us define reducers and generate associated actions
reducers: {
decrement: (state) => {
state.value -= 1;
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(fetchEmployees.pending, (state) => {
state.emplist = [];
state.loading = "loading";
});
builder.addCase(fetchEmployees.fulfilled, (state, { payload }) => {
state.emplist = payload;
state.loading = "loaded";
});
builder.addCase(fetchEmployees.rejected, (state, action) => {
state.loading = "error";
state.error = action.error.message;
});
},
});
export const { increment, decrement, incrementByAmount } = userSlice.actions;
export const selectCount = (state) => state.counter.value;
export const selectEmployees = createSelector(
(state) => ({
products: state.emplist,
loading: state.loading,
}),
(state) => state
);
export default userSlice.reducer;
my component goes here
import React, { useState, useEffect } from "react";
import EmployeeDetails from "./EmployeeDetails";
import { useSelector, useDispatch } from "react-redux";
import { fetchEmployees, selectEmployees } from "./features/auth/userSlice";
const Employelist = () => {
const [employe, setEmployee] = useState([]);
const dispatch = useDispatch();
const { emplist } = useSelector(selectEmployees);
React.useEffect(() => {
dispatch(fetchEmployees());
}, [dispatch]);
useEffect(() => {
emplist &&
emplist.then((res) => res.json()).then((data) => setEmployee(data));
}, []);
const handleclick = (id) => {
const emp = employe.filter((emp) => emp.employeeid !== id);
setEmployee(emp);
};
// console.log(currentUsers);
return (
<div>
<EmployeeDetails handleclick={handleclick} employe={employe} />
</div>
);
};
export default Employelist;
api.js
import axios from "axios";
export const base_emp = axios.get("http://localhost:5000/emp");
error message in console

How to get updated state immediately with redux?

After user submits a form I dispatched the create course function.
const handleCreateCourse = () => {
dispatch(createCourse(courseData));
// ??
};
How can I get and use the "_id" of newly created course immediately which will come from backend and will be saved in the updated state? So that I can do the following:
const courses = useSelector(state => state.courses);
const handleCreateCourse = () => {
dispatch(createCourse(courseData));
// ??
const newCourse = courses[courses.length-1];
history.push(`/course/${newCourse._id}`);
};
The createCourse function is using redux-thunk and looks like this:
export const createCourse = (course) => async (dispatch) => {
try {
const { data } = await api.createCourse(course);
dispatch({ type: CREATE_COURSE, payload: data });
} catch (error) {
console.log(error);
}
};
You can return API responses in thunk after dispatching the action.
E.g.
import { createStore, applyMiddleware, combineReducers } from 'redux';
import ReduxThunk from 'redux-thunk';
const thunk = ReduxThunk.default;
const api = {
async createCourse(name) {
return { data: { _id: '100', name } };
},
};
function coursesReducer(state = [], action) {
return state;
}
const rootReducer = combineReducers({
courses: coursesReducer,
});
const store = createStore(rootReducer, applyMiddleware(thunk));
const CREATE_COURSE = 'CREATE_COURSE';
export const createCourse = (course) => async (dispatch) => {
try {
const { data } = await api.createCourse(course);
dispatch({ type: CREATE_COURSE, payload: data });
return data;
} catch (error) {
console.log(error);
}
};
const handleCreateCourse = () => {
store.dispatch(createCourse({ name: 'math' })).then((newCourse) => {
console.log('newCourse: ', newCourse);
console.log(`/course/${newCourse._id}`);
});
};
handleCreateCourse();
The exeuction result:
newCourse: { _id: '100', name: { name: 'math' } }
/course/100

Categories

Resources