Re-render after delete item with redux thunk - reactjs

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.

Related

How to rerender when Redux state is changed

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.

Map fetched data in React with Redux Toolkit

i´m trying to fetch all the product i have in my DB (mongodb) using the fetch API. The result i get i store in a slice using Redux Toolkit. The problem is when i pass the data fetched and stored to a component as a props, the result is not beeing displayed.
slice
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
products: [],
};
const productSlice = createSlice({
name: "product",
initialState,
reducers: {
setProducts(state, action) {
state.products.push(action.payload);
},
},
});
export const { setProducts } = productSlice.actions;
export default productSlice.reducer;
store
import { configureStore } from "#reduxjs/toolkit";
import uiSlice from "./ui-slice";
import userSlice from "./user-slice";
import productSlice from './product-slice';
const store = configureStore({
reducer: {
ui: uiSlice,
user: userSlice,
products: productSlice
},
});
export default store;
function i used to fetch the products
export const fetchProduct = () => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:xx/xxx");
const data = await response.json();
let loadedProducts = [];
for (const key in data) {
loadedProducts.push({
id: data[key]._id,
productName: data[key].productName,
price: data[key].price,
imageUrl: data[key].imageUrl,
});
}
dispatch(setProducts(loadedProducts));
} catch (error) {
console.log(error);
}
};
I get the value stored in my redux state with useSelector and use it to fetch the products
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Container } from "react-bootstrap";
import { fetchProduct } from "../../actions/productActions";
import Hero from "../hero/Hero";
import Footer from "../footer/Footer";
import DisplayProductsList from "../displayProduct/DisplayProductsList";
export default function Home() {
const productsInfo = useSelector((state) => state.products.products);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchProduct());
}, []);
return (
<>
<Hero />
<DisplayProductsList products={productsInfo} />
<Container fluid>
<Footer></Footer>
</Container>
</>
);
}
And then i map it
export default function DisplayProductsList(props) {
console.log(props)
return (
<ul>
{props.products.map((product) => (
<DisplayProducts
key={product.id}
imageUrl={product.imageUrl}
name={product.productName}
price={product.price}
/>
))}
</ul>
);
}
console log the props i sent
if i console log the state in the selector this is what i get
code in the backend
module.exports.fetchProduct = async (req, res) => {
try {
const products = await Product.find({});
console.log(products)
if (products) {
res.json(products);
}
} catch (error) {
console.log(error);
}
};
As I can see in your code, you are storing an array of objects i.e. 'loadedProducts' into 'products' which is also an array. Something like this:
nested array
So in order to get the productsInfo in DisplayProductsList component, instead of doing this:
{props.products.map((product) => (
<DisplayProducts
key={product.id}
imageUrl={product.imageUrl}
name={product.productName}
price={product.price}
/>
))}
you should do something like this:
{props.products[0].map((product) => (
//rest of your code
))}

React/redux toolkit fetchapi issue -- I am trying to get userlist and its showing undefined

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);

react/redux toolkit fetching data - when refresh the page the state gone

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

state.cartItems is not iterable when i want to add a item to cart

I'm trying to learn mern stack and redux by making a small e-commerce website and i have this state.cartItmes is not iterable error in redux action and i have no idea what is causing it or how to fix it
So because i hope one day i will became a Frontend developer i'm doing what a developer does...i'm asking you guys what i did wrong in my spagetti code
Cart Reducer
export const cartReducer = (state = { cartItems: [] }, action) => {
if (action.type === "ADD_TO_CART") {
return { cartItems: [...state.cartItems, action.payload] };
}
return state;
};
Cart Action
import axios from "axios";
export const addToCart = (id) => async (dispatch, getState) => {
try {
const { data } = await axios.get(`/products/${id}`);
dispatch({ type: "ADD_TO_CART", payload: data });
localStorage.setItem(
"cartItems",
JSON.stringify(getState().cart.cartItems)
);
} catch (error) {
console.log(error.message);
}
};
Cart component
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addToCart } from "../actions/CartActions";
import { useParams } from "react-router-dom";
const Cart = () => {
const dispatch = useDispatch();
const { id } = useParams();
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
console.log(cartItems);
useEffect(() => {
dispatch(addToCart(id));
}, [dispatch, id]);
return (
<section>
<div className="cart">
<div className="cart-items">Cart</div>
<div className="buy-items"></div>
</div>
</section>
);
};
export default Cart;
After analyzing your code, I think the issue may be the empty array that you initially set. You should try using some default value as initial value, I think this should solve the issue.
Also, it is generally not a good practice to put async logic inside a reducer. You may want to implement some middleware for this. Here's some more info on it: https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-middleware-to-enable-async-logic

Resources