I develop a small application with the purpose of studying react hooks. The application consists of writing the name of an artist in a section. In the next section, a similar form, to fill with albums of each artist.
I have all my components separate, as well as the context, status, and reducer. I have a separate context for each artist, albums, and songs. When I click on an artist button, I want to filter the albums so that only theirs appear. But use reducer dispatch twice and in console log it tells me that both come from reducer file.
Something interesting is that when I click the first time, I only go through once, but the second time I see that I select a dispatch artist twice.
Another interesting thing is that when I change the selected artist, it removes the state albums, which is where I have all the albums. It does not filter them but removes them.
This is the form where I click to call the id of each artist and I sent the currently selected artist to artist context and album context
import React, { useContext } from "react";
import AlbumContext from "../../context/Album/AlbumContext";
import ArtistContext from "../../context/Artist/ArtistContext";
const Artist = ({ item }) => {
const albumContext = useContext(AlbumContext);
const { getArtist } = albumContext;
const artistContext = useContext(ArtistContext);
const { artist, getselectedArtist } = artistContext;
const handleSelect = (artist_id) => {
getArtist(artist_id);
getselectedArtist(artist_id);
};
return (
<>
<div
style={{ border: "1px solid black", margin: "10px", padding: "5px" }}
>
<p>{item.name}</p>
<button type="button">Edit</button>
<button type="button">Delete</button>
<button type="button" onClick={() => handleSelect(item.id)}>
Select
</button>
</div>
</>
);
};
export default Artist;
This second one is the ALBUMSTATE where I call the function getArtist to dispatch and get all the albums of that selected artist
import React, { useReducer } from "react";
import AlbumContext from "./AlbumContext";
import AlbumReducer from "./AlbumReducer";
import { GET_ARTIST, ADD_ALBUM, VALIDATE_FORM } from "../../types";
const AlbumState = (props) => {
const initialState = {
albums: [],
errorform: false,
selectedartist: "",
};
const [state, dispatch] = useReducer(AlbumReducer, initialState);
//-----METHODS-----//
const addAlbum = (album) => {
dispatch({
type: ADD_ALBUM,
payload: album,
});
};
const setError = (error) => {
dispatch({
type: VALIDATE_FORM,
payload: error,
});
};
const getArtist = (id) => {
dispatch({
type: GET_ARTIST,
payload: id,
});
};
return (
<AlbumContext.Provider
value={{
selectedartist: state.selectedartist,
albums: state.albums,
errorform: state.errorform,
addAlbum,
setError,
getArtist,
}}
>
{props.children}
</AlbumContext.Provider>
);
};
export default AlbumState;
This is the ALBUMREDUCER
import { GET_ARTIST, ADD_ALBUM, VALIDATE_FORM } from "../../types";
export default (state, action) => {
switch (action.type) {
case ADD_ALBUM:
return {
...state,
albums: [action.payload, ...state.albums],
};
case VALIDATE_FORM:
return {
...state,
errorform: action.payload,
};
case GET_ARTIST:
console.log(action.payload);
return {
...state,
selectedartist: action.payload,
albums: state.albums.filter(
(album) => album.artist_creator === action.payload
),
};
return {
...state,
selectedartist: action.payload,
};
default:
return state;
}
};
There are two things you need to consider here
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
- Class component constructor, render, and shouldComponentUpdate methods
- Class component static getDerivedStateFromProps method
- Function component bodies
- State updater functions (the first argument to setState)
- Functions passed to useState, useMemo, or useReducer
case GET_ARTIST:
console.log(action.payload, state.albums, "getArtistalbum");
return {
...state,
selectedartist: action.payload,
albums: state.albums.filter(
album => album.artist_creator === action.payload
) // This returns empty array
};
Last thing to note here is that you are overriding original albums array in redux with the filtered one and hence all your albums data will be removed. Instead keep a separete key for filtered items and filter it like
case GET_ARTIST: console.log(action.payload, state.albums, "getArtistalbum"); return { ...state, selectedartist: action.payload, selectedAlbums: state.albums.filter( album => album.artist_creator === action.payload ) // A different key for selected albums filtered from all albums };