I am new in redux and redux-toolkit. I am trying to get data from api.
I get error and can not receive data from api. I am using redux-toolkit library.
This is my App.js:
function App() {
const companies = useSelector(state => state.companyList);
console.log(companies)
return (
<div className="App">
<header className="App-header">
{companies.map(company => {
return(
<h1>{company.name}</h1>
)
})}
<h1>hello</h1>
</header>
</div>
);
}
export default App;
This is createSlice.js
const getCompanies = axios.get(
"https://mocki.io/v1/d4867d8b-b5d5-4a48-a4ab-79131b5809b8"
).then((response) => {
console.log(response.data)
return response.data;
}).catch((ex) => {
console.log(ex)
})
export const companySlice = createSlice({
name: "companyList",
initialState: {value: getCompanies},
reducers: {
addCompnay: (state, action) => {},
},
});
export default companySlice.reducer;
Here is store.js
import { configureStore } from "#reduxjs/toolkit";
import companyReducer from "./features/companySlice/compnayList";
export const store = configureStore({
reducer:{
companyList: companyReducer,
}
})
In the browser, I receive this error:
enter image description here
You are making a lot of mistakes here so be sure to check out some tutorials and docs: https://redux-toolkit.js.org/tutorials/quick-start
You need to use createAsyncThunk and handle the response in extraReducers: https://redux-toolkit.js.org/rtk-query/usage/migrating-to-rtk-query#implementation-using-createslice--createasyncthunk
In companySlice:
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
export const getCompanies = createAsyncThunk(
"companyList/getCompanies",
async () => {
try {
const response = await axios.get(
"https://mocki.io/v1/d4867d8b-b5d5-4a48-a4ab-79131b5809b8"
);
return response.data;
} catch (error) {
console.error(error);
}
});
const companySlice = createSlice({
name: "companyList",
initialState: {
company: {},
isLoading: false,
hasError: false
},
extraReducers: (builder) => {
builder
.addCase(getCompanies.pending, (state, action) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(getCompanies.fulfilled, (state, action) => {
state.company = action.payload;
state.isLoading = false;
state.hasError = false
})
.addCase(getCompanies.rejected, (state, action) => {
state.hasError = true
state.isLoading = false;
})
}
});
// Selectors
export const selectCompanies = state => state.companyList.company;
export const selectLoadingState = state => state.companyList.isLoading;
export const selectErrorState = state => state.companyList.hasError;
export default companySlice.reducer;
Then you import selectCompanies wherever you want to use it and access it with useSelector.
In App.js:
import { useSelector } from "react-redux";
import { selectCompanies } from "WHEREEVER selectCompanies IS EXPORTED FROM";
function App() {
// Company list state
const companies = useSelector(selectCompanies);
// Rest of the code
.....
.....
.....
.....
}
export default App;
Related
Hi folks I am new to React just learning reduxjs/toolkit for application state management. I have created simple application for handling posts. Currently what I am doing is loading list of posts using async Thunk called fetchPosts. My async thunk is being called twice on application load and I have literally no idea what is making to do so. I am using react version 18.2.0 and here is my post slice.
import { createAsyncThunk, createSlice, nanoid } from "#reduxjs/toolkit";
import {sub} from 'date-fns';
import axios from 'axios';
const POST_URL = 'http://jsonplaceholder.typicode.com/posts';
const initialState = {
posts : [],
status : 'idle',
error : null
};
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
const response = await axios.get(POST_URL);
return response.data;
});
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers:{
postAdded: {
reducer(state, action) {
state.posts.push(action.payload);
},
prepare(title,content, userId){
return {
payload:{
id: nanoid(),
title,
content,
userId,
date: new Date().toISOString(),
reactions: {
thumbsUp: 0,
wow:0,
heart:0,
rocket:0,
coffee:0
}
}
}
}
},
reactionAdded(state, action) {
const {postId, reaction} = action.payload;
const existingPost = state.posts.find(post => post.id === postId);
if (existingPost) {
existingPost.reactions[reaction]++;
}
}
},
extraReducers(builder){
builder.addCase(fetchPosts.pending, (state, action) => {
state.status = 'loading';
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = 'succeeded';
let min = 1;
const loadedPosts = action.payload.map(post =>
{
post.date = sub(new Date(), {minutes: min++}).toISOString();
post.reactions = {
thumbsUp: 0,
wow: 0,
heart:0,
rocket:0,
coffee:0
}
return post;
});
state.posts = loadedPosts;
})
.addCase(fetchPosts.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message
})
}
});
export const {postAdded,reactionAdded} = postsSlice.actions;
export const selectAllPosts = (state) => state.posts.posts;
export const getPostsStatus = (state) => state.posts.status;
export const getPostsError = (state) => state.posts.error;
export default postsSlice.reducer;
And here is my Post list component.
import React from 'react'
import { useSelector, useDispatch } from 'react-redux';
import { selectAllPosts, getPostsError, getPostsStatus, fetchPosts } from './postsSlice';
import { useEffect } from 'react';
import PostExcerpt from './PostExcerpt';
const PostsList = () => {
const dispatch = useDispatch();
const posts = useSelector(selectAllPosts);
const postStatus = useSelector(getPostsStatus);
const postError = useSelector(getPostsError);
useEffect(() => {
console.log('post status: ', postStatus);
if(postStatus === 'idle')
{
dispatch(fetchPosts());
}
},[postStatus])
let content;
if(postStatus === 'loading'){
console.log('loading');
content = <p>"Loading..."</p>
}
else if(postStatus === 'succeeded'){
console.log('succeded');
const orderedPosts = posts.slice().sort((a,b) => b.date.localeCompare(a.date));
content = orderedPosts.map(post => <PostExcerpt key={post.id} post={post}/>);
}
else if (postStatus === 'failed'){
content = <p>{postError}</p>;
}
return (
<section>
<h2>Posts</h2>
{content}
</section>
);
}
export default PostsList
So this was happening due to the mount -> unmount -> remount behaviour of component implemented in react version 18 useEffect.
I am trying to auth session by random user with http get request and createAsyncThunk.
fetching the user data in App.js on mount hook.
I can see the get request in my network and the new fetched state in redux dev tool,
but my TopBar.js useSelector return the initial state before the fetch.
TopBar.js user log:
App.js:
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchRandomUserData())
}, [dispatch]);
authSlice.js:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
const initialState = {
isLoggedIn: true,
user: {},
loading: false,
error: null,
};
export const fetchRandomUserData = createAsyncThunk(
'auth/fetchRandomUser',
async () => {
try {
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
return data.results[0];
} catch (error) {
throw Error(error);
}
}
);
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
logout(state, action) {
state.isLoggedIn = false;
},
},
extraReducers: {
[fetchRandomUserData.pending]: (state, action) => {
state.loading = true;
state.error = null;
},
[fetchRandomUserData.fulfilled]: (state, action) => {
console.log("action.payload",action.payload);
state.user = action.payload;
state.loading = false;
},
[fetchRandomUserData.rejected]: (state, action) => {
state.error = action.error.message;
state.loading = false;
},
},
});
export const { logout } = authSlice.actions;
export default authSlice.reducer;
store.js
import { configureStore } from '#reduxjs/toolkit';
// import booksReducer from './reducers/booksReducer';
import booksReducer from './slices/bookSlice';
import authReducer from './slices/authSlice';
const store = configureStore({
reducer: { books: booksReducer, auth: authReducer },
});
export default store;
TopBat.js:
export default function TopBar(): JSX.Element {
const user = useSelector((state: any) => state.auth);
console.log("topbar",user); // returns the initial state
//....
Please make sure that you update react-redux to version 8 if you are using react 18.
There are known cases where components just stop updating if you are using react-redux 7 and lower with react 18.
I am working on a project and I need to fetch data from backend or from an API. I tried fetch the data but nothing appears. I think I am doing something wrong in the container. I am a beginner in react-redux, I don't know what I am doing wrong.
I've already read all the posts but nothing seems to works.
my reducer:
const initialState={
articles: [],
};
const rootReducer = (state = initialState, action) => {
const { type, payload }=action;
switch(type) {
case SRETRIEVE_ARTICLE:{
return {
...state,
articles:payload,
};
}
default: return state;
}
}
export default rootReducer;
This is what I have right now in container:
import Articles from 'components/Articles';
import { fetchArticles } from '../../pages/index';
const mapStateToProps = (state) => ({
articles:state.articles
})
const ConnectedArticles = connect(
mapStateToProps,
{fetchArticles}
)(Articles)
export default ConnectedArticles;
pages.js
axios.get('API').then((response) => {
const { data } = response;
dispatch({ type: RETRIEVE_ARTICLES, payload: data });
});
};
const Index = () => {
const articles= useSelector((state) => state.articles);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchArticles);
}, []);
return <>{articles && articles.map((article) => <Article key={article.id} name={article.name} />)}</>;
};
Index.getInitialProps = async () => ({
authRequired: true,
label: 'Dashboard',
});
export default Index;
Also I defined the action type: export const SET_UNOPENED_REWARD = 'SET_UNOPENED_REWARD';
and action const unopenedRewards = (payload) => ({ type: SET_UNOPENED_REWARD, payload });
One very nice way to do data fetching with redux is to use redux toolkit's createAsyncThunk and createSlice functions.
// src/features/articles/articlesSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
export const fetchArticles = createAsyncThunk("articles/get", async () => {
// Here you can use axios with your own api
const response = await fetch("https://rickandmortyapi.com/api/character");
const json = await response.json();
return json.results;
});
export const slice = createSlice({
name: "articles",
initialState: {
loading: false,
data: []
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchArticles.pending, (state) => {
state.loading = true;
});
builder.addCase(fetchArticles.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = false;
});
builder.addCase(fetchArticles.rejected, (state) => {
state.loading = false;
});
}
});
export default slice.reducer;
// src/features/articles/Articles.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { fetchArticles } from "./articlesSlice";
export const Articles = () => {
const articles = useSelector((state) => state.articles.data);
const loading = useSelector((state) => state.articles.loading);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchArticles());
}, []);
return (
<>
{loading && "...loading"}
{articles.map((article) => <Article key={article.id} {...article} />)}
</>
);
};
you should use async and await
let response = await axios.get('https://run.mocky.io/v3/5c045896-3d18-4c71-a4e5-5ed32fbbe2de')
if(response.status==200){
dispatch({ type: RETRIEVE_ARTICLES, payload: data });
}
in below react/ redux toolkit app , in userslice file I am trying to export my entities piece of state and import in main file , when I try to console in comes undefined , not sure why its undefined ,
but When I trying to pull the {entities} directly form state its working fine, would like to know why its showing undefined in console, if anyone knows please check ?
below is the state part which I am getting undefined
export const { SelectUserList } = (state) => state.userslist.entities;
below is the console which shows undefiend
console.log(SelectUserList);
my slice file is below
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
export const fetchuserlist = createAsyncThunk(
"userslist/fetchusers",
async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const users = await response.json();
return users;
}
);
const userSlice = createSlice({
name: "userslist",
initialState: {
entities: [],
loading: false,
},
reducers: {
// userAdded(state, action) {
// state.entities.push(action.payload);
// },
},
extraReducers: {
[fetchuserlist.pending]: (state, action) => {
state.loading = true;
},
[fetchuserlist.fulfilled]: (state, action) => {
state.entities = [...state.entities, ...action.payload];
state.loading = false;
},
[fetchuserlist.rejected]: (state, action) => {
state.loading = false;
},
},
});
export const { userAdded, userUpdated, userDeleted } = userSlice.actions;
export const { SelectUserList } = (state) => state.userslist.entities;
export default userSlice.reducer;
me component file is below
import React from "react";
import { fetchuserlist, SelectUserList } from "./features/userSlice";
import { useDispatch, useSelector } from "react-redux";
const Mainlist = () => {
const dispatch = useDispatch();
const { entities } = useSelector((state) => state.users);
console.log(SelectUserList);
return (
<div>
<button onClick={() => dispatch(fetchuserlist())}>Load list</button>
{entities?.map((s) => (
<div className="user_list" key={s.id}>
<h4>{s.id}</h4>
<h6>{s.email}</h6>
<button>delete</button>
<button>edit</button>
</div>
))}
</div>
);
};
export default Mainlist;
In your slice you are declaring the function in the wrong way. You should declare the SelectUserList function like this:
export const SelectUserList = (state) => state.userslist.entities;
In your component file you should access the entities returned in SelectUserList with a useSelector. Like this:
const usersListEntities = useSelector(SelectUserList);
I created an action creator that is simply supposed to make a get request to my API and return with a list of all projects. However, for some reason, my return dispatch in my thunk function is not firing at all. It gets to the console.log() statement and just ends. There are no consoles errors, and no network calls being made either as far as I can tell. Any ideas why it would do absolutely nothing?
Dashboard.js (component)
import ProjectItem from "../Project/ProjectItem";
import styles from "./Dashboard.module.css";
import CreateProjectButton from "../CreateProjectButton/CreateProjectButton";
import { connect } from "react-redux";
import { getProjects } from "../../Redux/getProjects/actions";
const Dashboard = props => {
useEffect(() => {
console.log("blah");
getProjects();
}, []);
return (
<div className={styles.dashboardContainer}>
<h1>Projects</h1>
<br />
<CreateProjectButton />
<br />
<hr />
<ProjectItem />
</div>
);
};
const mapStateToProps = state => {
return {
projects: state
};
};
const mapDispatchToProps = dispatch => {
return {
getProjects: () => dispatch(getProjects())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
action.js (action creator)
import { GET_PROJECTS_SUCCESS, GET_PROJECTS_ERROR } from "./constants";
export const getProjectsSuccess = payload => {
console.log("getProjectSuccess", payload);
return {
type: GET_PROJECTS_SUCCESS,
payload
};
};
export const getProjectsError = () => {
console.log("there was an error");
return {
type: GET_PROJECTS_ERROR
};
};
export function getProjects() {
console.log("getProject");
return dispatch => {
axios
.get("/project/all")
.then(res => dispatch(getProjectsSuccess(res.data)))
.catch(err => dispatch(getProjectsError(err)));
};
}
index.js (getProject reducer)
const initialState = {
projects: [], //array of projects
project: {}, // single project for update case
reRender: false
};
const getProjectsReducer = (state = initialState, action) => {
switch (action.type) {
case GET_PROJECTS_SUCCESS:
return { ...state, projects: action.payload }; // will need to change action.payload later on
default:
return state;
}
};
export default getProjectsReducer;
constants.js
export const GET_PROJECTS_SUCCESS = "GET_PROJECTS_SUCCESS";
export const GET_PROJECTS_ERROR = "GET_PROJECTS_ERROR";
rootReducer.js
import createProjectReducer from "./createProject/index";
import getProjectsReducer from "./getProjects/index";
const rootReducer = (state = {}, action) => {
return {
project: createProjectReducer(state.project, action),
projects: getProjectsReducer(state.projects, action)
};
};
export default rootReducer;
FIXED: After reading up on the use effect hook in functional components I realized I was missing props.getProjects in the useEffect function in dashboard.js!