Redux wont remove item from state array by ID - reactjs

I dont understant what is wrong with this code, im passing in ID and filtering out state, but it just wont remove, cant figure it out, would love some help.
slice:
import { createSlice } from "#reduxjs/toolkit";
const movieSlice = createSlice({
name: "movie",
initialState: { favoriteMovies: [] },
reducers: {
addMovie: (state, action) => {
state.favoriteMovies = [...state.favoriteMovies, action.payload];
},
removeMovie: (state, action) => {
state.favoriteMovies = state.favoriteMovies.filter(
(movie) => movie.imdbID !== action.payload
);
},
},
});
export const { addMovie, removeMovie } = movieSlice.actions;
export const selectMovie = (state) => state.movie.favoriteMovies;
export default movieSlice.reducer;
dispatching:
const MovieDetail = ({ movie }) => {
const [isFavorite, setIsFavorite] = useState(false);
const dispatch = useDispatch();
const imdbID = movie.imdbID;
const handleAddFavorite = () => {
dispatch(addMovie({ movie }));
setIsFavorite(true);
};
const handleRemoveFavorite = () => {
dispatch(removeMovie({ imdbID }));
console.log(imdbID);
setIsFavorite(false);
};
it does nothing when should remove, and then add it again. The ids i pass in are correct.

The way you are dispatching with
dispatch(removeMovie({ imdbID }));
imdbID will end up as action.payload.imdbID.
So you need to either access that, or dispatch like
dispatch(removeMovie(imdbID));

Related

having problem in set payload in redux using createSlice() and createasyncthunk()

component where I am using the state data
const { contentTitles: ContentTitles } = useSelector((state) => state);
const dispatch = useDispatch();
useEffect(() => {
const fetchData = async () => {
const response = await dispatch(getContentTitles()).unwrap();
};
fetchData();
}, [ContentTitles]);
slice
const contentTitles = JSON.parse(localStorage.getItem("contentTitles"));
export const getContentTitles = createAsyncThunk("contenttitles/getContenttitles", async (thunkAPI) => {
try{
const response = await contentitleService.getContenttitles();
return { contentTitles: response };
} catch (error) {
const message =
(error.response &&
error.response.responsedata &&
error.response.responsedata.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
});
const initialState = contentTitles ? contentTitles : null
const contenttitleSlice = createSlice({
name: "contenttitles",
initialState,
reducers: (state, action) => {
state.contentTitles = action.payload.contentTitles;
}
});
const { reducer } = contenttitleSlice;
export default reducer;
Can anyone tell me that why my data is not getting set to the redux? I am new to the redux and asyncthunk. I can't find the reason of not getting my redux state updated.
You have to define an extra actions (extraReducers) for this. Since your codebase is not clear to me, I will use a different example to explain it to you.
// First, create the thunk
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId: number, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
const initialState = {
user: null
}
const usersSlice = createSlice({
name: 'users',
initialState,
reducers: {
// Define your other actions here
},
extraReducers: (builder) => {
// Add reducers for additional action types here, and handle loading state as needed
builder.addCase(fetchUserById.fulfilled, (state, action) => {
// Add user to the state array
state.user = action.payload;
})
},
})
As you can see here, after the request completed, it will either be a success or error response. You have to define extra reducers to catch this. Above example shows a successful scenario. But you can define extra actions for following phases as well.
pending: 'users/requestStatus/pending'
fulfilled: 'users/requestStatus/fulfilled'
rejected: 'users/requestStatus/rejected'
const initialState = contentTitles ? {contentTitles} : {contentTitles: null}
const contenttitleSlice = createSlice({
name: "contenttitles",
initialState,
extraReducers: {
[getContentTitles.fulfilled]: (state, action) => {
state.contentTitles = action.payload.contentTitles
},
},
});
Yes, the extraReducers were missing. The above code of adding extraReducers in my specific scenario solved the problem.

useEffect goes to cyclic dependency with Redux Toolkit

Tryed to render component using useEffect with "product" dependency but it goes cyclic dependency.
Tryed to use prev state but it doesn't help.
React don't gives any error, but useEffect send request every second.
Component:
export const ProductPage = (props: any) => {
const { product, isLoading, error } = useTypedSelector(state => state.Product)
const dispatch = useTypedDispatch()
const { id } = useParams()
const prevProd = usePrevious(product)
useEffect(() => {
if (prevProd !== product){dispatch(fetchProduct(Number(id)))}
}, [product])
return (
<div>
<div>{product.name}</div>
<div>{product.id}</div>
<div>{product.price}</div>
</div>
)
}
Async Thunk:
export const fetchProduct = createAsyncThunk(
'product/fetch',
async (id: number, thunkApi) => {
try {
const response = await productService.fetch(id)
return response.data
} catch (error: any) {
return thunkApi.rejectWithValue(error.message)
}
}
)
Slice:
export const ProductSlice = createSlice({
name: 'product',
initialState,
reducers: {},
extraReducers: {
[fetchProduct.fulfilled.type]: (state, action: PayloadAction<IProductData>) => {
state.error = ''
state.isLoading = false
state.product = action.payload
},
[fetchProduct.pending.type]: (state) => {
state.isLoading = true
},
[fetchProduct.rejected.type]: (state, action: PayloadAction<string>) => {
state.isLoading = false
state.error = action.payload
},
}
})
Explain please, why this problem occured and how resolve this?

having problem with use dispatch in redux

I try to get firestore database and dispatch this database in redux. and print this database in my listpage.
I succeed to get firestore database, and console.log are print all data. but I try to use map() function. print only one data. I don't know why this had happened.
I think my code is wrong. but I don't know where I got it wrong.
DictCard.js
import { collection, getDocs} from "firebase/firestore";
import React, { useEffect } from "react";
import { db } from "../firebase";
import { useSelector, useDispatch } from "react-redux";
import { getDict } from "../redux/ListReducer";
const Card = ({dict}) => {
return(
<div className="inbox">
<p className="text1">단어</p>
<p className="text2">{dict.word}</p>
<p className="text1">설명</p>
<p className="text2">{dict.explain}</p>
<p className="text1">예시</p>
<p className="text2" style={{color:"lightskyblue",paddingBottom:"0"}}>{dict.example}</p>
</div>
)
}
const DictCard = () => {
const dictList = useSelector((state) => state.dictList.dicts);
const dispatch = useDispatch();
useEffect( async () => {
const query = await getDocs(collection(db, "dict"));
query.forEach(doc => {
console.log([doc.id, doc.data()])
dispatch(getDict([{id: doc.id, text: doc.data()}]))
});
},[]);
return(
<div className="dict-card" >
{dictList.map((dict) => (
<Card dict = {dict.text} key = {dict.id}/>
))}
</div>
)
}
export default DictCard;
ListReducer.js
import { createSlice } from "#reduxjs/toolkit";
// const initialState = [{id:"",text:{word:"",explain:"",example:""}}]
const initState = {
dicts: [{
id:"",
text:{
word:"",
explain:"",
example:""
}
},]
}
export const ListReducer = createSlice({
name: "dictList",
initialState: initState,
reducers: {
addDict: (state, action) => {
state.dicts = action.payload
},
getDict: (state, action) => {
state.dicts = action.payload
},
updateDict: (state, action) => {
},
deleteDict: (state, action) => {
},
},
});
export const { addDict, getDict, updateDict, deleteDict } = ListReducer.actions;
export default ListReducer.reducer;
I think dispatch's position is wrong but i have no idea
I solved problem.
useEffect( async () => {
const arr = []
const query = await getDocs(collection(db, "dict"));
query.forEach(doc => {
console.log([doc.id, doc.data()])
// dispatch(getDict([{id: doc.id, text: doc.data()}]))
arr.push({id: doc.id, text: doc.data()})
});
dispatch(getDict(arr))
},[]);
need to make empty array
Try not to use async function for useEffect
useEffect(() => {
const fetchData = async () => {
const query = await getDocs(collection(db, "dict"));
query.forEach(doc => {
console.log([doc.id, doc.data()])
dispatch(getDict([{id: doc.id, text: doc.data()}]))
});
}
fetchData()
},[]);
I think the issue may caused from the useEffect function. If it not, please comment below so i can track the issue more clearly

I need to rerender functional component

I have a component
const MovieDetail = (props) => {
//const [state, setstate] = useState(initialState)
const { id } = useParams();
const movie = useSelector(getMovie)
const movies = useSelector(getAllMovies)
const dispatch = useDispatch();
useEffect(() => {
fetchMovieById(id)
.then((response) => dispatch(addMovie(response)))
}, [movies])
const onSubmit = (fromData) => {
//here
dispatch(addComments({fromData, id, movies}));
console.log(fromData);
}
return (
<div className='container'>
<br/>
{
<div className='detail'>
<div>
<h2>{movie.title}</h2>
<div>
<img src={movie.img} alt={movie.title}/>
</div>
</div>
<div className='description'>
<div>
{movie.details}
</div>
<div className='comments'>
<h5>Stay your comment here</h5>
<CommentReduxForm onSubmit={onSubmit}/>
</div>
<div className='comments'>
{movie.comments ? movie.comments : <div>no comments yet</div>}
</div>
<div className='ratio'>
<strong>Ratio: {movie.ratio}</strong>
</div>
</div>
</div>
}
</div>
)
}
export default MovieDetail
I'm using redux toolkit, I'm trying to add comment to movie. I come on the site and select the movie, in the form I put my comment, then I click bottun to add comment, then I go to devtools in my browser and I see My comment in the state, but on the page, I see "there are not comments", so I have updated state, but not updated page, and i think I need to rerender my component, or maybe I need another technic?
Also there is my slice with actions and reducers, help me please if anybody can
const initialState = {
movies: [],
movie: {}
}
const movieSlice = createSlice({
name: "movies",
initialState,
reducers: {
addMovies: (state, {payload}) => {
state.movies = payload;
},
addMovie: (state, action) => {
state.movie = action.payload[0];
},
addComments: (state, action) => {
debugger
let author = action.payload.fromData.yourName;
let comment = action.payload.fromData.yourComment;
let movieId = action.payload.id;
let moviesArr = action.payload.movies;
let obj = moviesArr.find((item) => item.id == movieId);
let newObj = {...obj, comments: comment}
const newArr = moviesArr.map(o => {
if (o.id === newObj.id) {
return newObj;
}
return o;
})
state.movies = newArr;
}
},
});
export const {addMovies, addMovie, addComments} = movieSlice.actions;
export const getAllMovies = (state) => state.movies.movies;
export const getMovie = (state) => state.movies.movie;
export default movieSlice.reducer;
In Redux, reducers are not allowed to mutate the original / current state values
You can see these rules in reducer rules
So you should return an updated state instead of changing the oringials
const initialState = {
movies: [],
movie: {}
}
const movieSlice = createSlice({
name: "movies",
initialState,
reducers: {
addMovies: (state, {payload}) => {
return {movies:payload,...state}
},
addMovie: (state, action) => {
return {movie:action.payload[0],...state}
},
addComments: (state, action) => {
let author = action.payload.fromData.yourName;
let comment = action.payload.fromData.yourComment;
let movieId = action.payload.id;
let moviesArr = action.payload.movies;
let obj = moviesArr.find((item) => item.id == movieId);
let newObj = {...obj, comments: comment}
const newArr = moviesArr.map(o => {
if (o.id === newObj.id) {
return newObj;
}
return o;
})
return {movies:newArr,...state}
}
},
});
export const {addMovies, addMovie, addComments} = movieSlice.actions;
export const getAllMovies = (state) => state.movies.movies;
export const getMovie = (state) => state.movies.movie;
export default movieSlice.reducer;

react-redux: infinite loop on dispatch

I have a sample application that loads entries from a Spring boot backend. However, my approach leads to an infinite loop that I cannot explain to myself.
api.ts
class CommonApi extends BaseApi {
public loadEntries = () => this.get('http://localhost:8080/radars/development/entries') as Promise<any>;
}
entriesSlice.ts
interface EntriesState {
map: {}
}
const initialState: EntriesState = {
map: {}
};
export const entriesSlice = createSlice({
name: 'entries',
initialState,
reducers: {
getEntries: (state, action: PayloadAction<any>) => {
state.map = action.payload;
},
},
});
export const { getEntries } = entriesSlice.actions;
export const getEntriesAction = (): AppThunk => dispatch => {
return commonApi.loadEntries().then(payload => {
const newPayload: any[] = [];
payload.map((entry: any) => {
return newPayload.push({
label: entry.label,
quadrant: toSegment(entry.category),
ring: toRing(entry.status)
})
})
dispatch(getEntries(newPayload));
}).catch(err => {
console.error('error: ', err)
})
};
export const entriesObject = (state: RootState) => state.entries.map;
export default entriesSlice.reducer;
I think I've found out that this line in entriesSlice.ts causes the error, but I dont know why:
state.map = action.payload;
App.tsx
import { entriesObject, getEntriesAction } from "../../features/entries/entriesSlice";
import { config1Object, getConfig1Action } from "../../features/config1/config1Slice";
function App() {
const config1 = useSelector(config1Object) as any;
const entries = useSelector(entriesObject) as any;
const dispatch = useDispatch();
const [value, setValue] = useState(0);
useEffect(() => {
dispatch(getConfig1Action());
dispatch(getEntriesAction());
}, [config1, entries, dispatch]);
return (
<Container>
<TabPanel value={value} index={0}>
<Chart config={config1} entries={entries} />
</TabPanel>
</Container>
);
}
What am I doing wrong?
You have entries as a dependency to your useEffect - every time getEntriesAction is dispatched it fetches entries and creates a new object in state, which tells react that entries has been updated (it's a new object with a new reference), which reruns the useEffect, which dispatches getEntriesAction again, which... leads to an infinite loop.

Resources