I am creating a react/redux toolkit app , I am saving data into redux , this is like a posts page and when user clicks a post it goes to single post page,
the data is saving in redux state and working fine, but when I refresh the detail page the redux data is gone not sure why its happening I want to keep data even if user refresh the page also.
my posts slice
import {
createSlice,
// createSelector,
// PayloadAction,
createAsyncThunk,
} from "#reduxjs/toolkit";
import axios from "axios";
export const fetchAllPostsAsync = createAsyncThunk(
"posts/fetchAllPostsAsync",
async (_, thunkAPI) => {
try {
const response = await axios.get("http://localhost:5000/posts/");
return response.data;
} catch (error) {
throw thunkAPI.rejectWithValue({ error: error.message });
}
}
);
export const postsSlice = createSlice({
name: "posts",
initialState: {
allposts: [],
loading: "idle",
error: "",
selectedPost: {},
},
reducers: {
selectedpost: (state, action) => {
state.selectedPost = action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(fetchAllPostsAsync.pending, (state, action) => {
state.allposts = [];
state.loading = "Loading";
});
builder.addCase(fetchAllPostsAsync.fulfilled, (state, action) => {
state.allposts = action.payload;
state.loading = "loaded";
});
builder.addCase(fetchAllPostsAsync.rejected, (state, action) => {
state.allposts = "data not loaded";
state.loading = "error";
state.error = action.error.message;
});
},
});
export const { selectedpost } = postsSlice.actions;
export default postsSlice.reducer;
my posts page
import React, { useEffect } from "react";
import SinglePost from "./SinglePost";
import { useSelector, useDispatch } from "react-redux";
import { fetchAllPostsAsync } from "../features/posts/postsSlice";
const Posts = () => {
const dispatch = useDispatch();
const posts = useSelector((state) => state.posts.allposts);
console.log(posts);
useEffect(() => {
dispatch(fetchAllPostsAsync());
}, [dispatch]);
return (
<div>
<button>Add new </button>
{posts?.map(({ _id, ...rest }) => (
<SinglePost key={_id} rest={rest} id={_id} />
))}
</div>
);
};
export default Posts;
my details page
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { selectedpost } from "../features/posts/postsSlice";
const Details = () => {
const { postId } = useParams();
const [postshere, setPostshere] = useState(selectedpost);
const dispatch = useDispatch();
const posts = useSelector((state) => state.posts.allposts);
const selectedpostsingle = useSelector((state) => state.posts.selectedPost);
const selectedPostArray = posts?.filter((post) => post._id === postId);
useEffect(() => {
dispatch(selectedpost(setPostshere(selectedPostArray[0])));
}, [dispatch]);
console.log(postId, "ffffff");
return (
<div>
<div key={postshere._id}>
{postshere.title} {postshere.message}
</div>
</div>
);
};
export default Details;
this is single post page
import React from "react";
import { Link } from "react-router-dom";
const SinglePost = ({ rest, id }) => {
// let history = useHistory();
// function handleClick() {
// history.push(`/${rest._id}`);
// }
console.log(rest);
return (
<Link to={`/details/${id}`}>
<div className="post">
<div className="post__title">{rest.title}</div>
<div className="post__body">{rest.message}</div>
<div className="post__body">{rest.date}</div>
</div>
</Link>
);
};
export default SinglePost;
error message
×
TypeError: Cannot read property 'title' of undefined
Details
src/components/Details.js:20
17 | console.log(postId, "ffffff");
18 | return (
19 | <div>
> 20 | <div key={postshere._id}>
| ^ 21 | {postshere.title} {postshere.message}
22 | </div>
23 | </div>
View compiled
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 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;
Hi I have 2 components.
The first component provides a read (useSelector) from the Redux state object and renders its contents
The second component ensures the insertion of new data into the same Redux state object
How to achieve that when a Redux state object changes with the help of the second component, the first component captures this change and renders the new content of the object again.
I tried to add in the component element:
useEffect(() => {
...some actions
}, [reduxStateObject]);
But it gives me too many requests.
/// EDIT add real example
component
import React from "react";
import { useSelector } from "react-redux";
const ToDoList = () => {
const { todos } = useSelector((state) => state.global);
return (
<div>
<h1>Active</h1>
{todos
?.filter((todo) => !todo.isCompleted)
.sort((a, b) => (a.deadline < b.deadline ? 1 : -1))
.map((todo, id) => {
const date = new Date(todo.deadline).toLocaleString();
return (
<div key={id}>
<p>{todo.text}</p>
<p>{date}</p>
</div>
);
})}
</div>
);
};
export default ToDoList;
component
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { getToDoItems } from "../redux/globalSlice";
import ToDoList from "../components/ToDoList";
const ToDoWall = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getToDoItems(1));
}, [dispatch]);
const submitForm = (e) => {
dispatch(postToDoItem(e.data));
};
return (
<>
<ToDoList />
<form onSubmit={submitForm}>
<input type="text"></input>
<input type="submit" value="" />
</form>
</>
);
};
export default ToDoWall;
/// EDIT add Reducer
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: null,
};
export const globalSlice = createSlice({
name: "global",
initialState,
reducers: {
setItems: (state, action) => {
state.todos = action.payload;
},
},
});
export const { setItems } = globalSlice.actions;
export default globalSlice.reducer;
// Load todo items
export const getToDoItems = (id) => {
return (dispatch) => {
axios
.get(`https://xxx.mockapi.io/api/list/${id}/todos`)
.then((resp) => dispatch(setItems(resp.data)));
};
};
// Post a list name
export const postNameList = (data) => {
return (dispatch) => {
axios.post("https://xxx.mockapi.io/api/list", {
name: data,
});
};
};
// Post a todo item
export const postToDoItem = (id, data) => {
return (dispatch) => {
axios.post(
`https://xxx.mockapi.io/api/list/${id}/todos`,
{
listId: id,
title: data.title,
text: data.text,
deadline: +new Date(data.deadline),
isCompleted: false,
}
);
};
};
As far as I understood, you don't need to do anything. When you dispatch action to change state in redux store, it'll change, and all components that use that state will get it, you don't need to worry about updating anything.
I have a problem updating and displaying data from the api.
The problem occurs when I delete a specific item, sometimes the change is displayed (rendered) and other times not, until after the refresh.
Where can there be a mistake please?
Component
import React, { useEffect } from "react";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { getTodoItems, deleteTodoItem } from "../redux/todoSlice";
const TodoList = () => {
const { id } = useParams();
const dispatch = useDispatch();
const { todoList, urlParams } = useSelector((state) => state.todo);
useEffect(() => {
dispatch(getTodoItems(id));
dispatch(setUrlParams(+id));
}, [dispatch, id]);
const handleClick = (e) => {
dispatch(deleteTodoItem(urlParams, e.target.id));
dispatch(getTodoItems(urlParams));
};
return (
<div>
{todoList.map((todo, id) => {
<div key={id}>
<p>{todo.text}</p>
<button id={todo.id} onClick={handleClick}>
Delete
</button>
</div>;
})}
</div>
);
};
export default TodoList;
Slice
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
todoList: null,
urlParams: null,
};
export const todoSlice = createSlice({
name: "todo",
initialState,
reducers: {
setItems: (state, action) => {
state.todoList = action.payload;
},
setUrlParams: (state, action) => {
state.urlParams = action.payload;
},
},
});
export const { setItems, setUrlParams } = todoSlice.actions;
export default todoSlice.reducer;
// Load todo items
export const getTodoItems = (id) => {
return (dispatch) => {
axios
.get(`https://xxx.mockapi.io/api/list/${id}/todolist`)
.then((resp) => dispatch(setItems(resp.data)));
};
};
// Delete todo item
export const deleteTodoItem = (idList, idTodo) => {
return (dispatch) => {
axios
.delete(`https://xxx.mockapi.io/api/list/${idList}/todolist/${idTodo}`)
.then(
axios
.get(`https://xxx.mockapi.io/api/list/${idList}/todolist`)
.then((resp) => dispatch(setItems(resp.data)))
);
};
};
Thus, the first comes to download data from api -> render this data -> delete a specific item -> re-render the list without the deleted item
The problem occurs in the re-rendering, sometimes it happens that the data is erased in the background but the change is not apparent, but sometimes absolutely nothing happens, and the change occurs only after clicking the delete button again.
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);