I have an array "movies" of objects like this:
{
id: "some id",
title: "some title",
actors: []
}
As you can see, the actors array stays empty, because I don't add actors while I am adding a movie. I want to add actors later on, while being on http://localhost:3000/movies/<movieId>
.
I do this by this Form. Im just selecting a actor, from already existing lists of actors.
const PostForm = ({ addActorToMovie, movie, actors, history }, props) => {
const handleSubmit = (values) => {
addActorToMovie(values);
// history.push(`/movies/${movie.id}`);
};
let actorsList =
actors.length > 0 &&
actors.map((item, i) => {
return (
<option value={`${item.firstName}, ${item.lastName}`}>
{item.firstName item.lastName}{" "}
</option>
);
});
return (
<div>
<h3>Add Actor</h3>
<Formik
initialValues={{
actor: "",
}}
onSubmit={(values) => handleSubmit(values)}
enableReinitialize={true}
>
<Form>
<Field name="actor" component="select">
<option value="null"> </option>
{actorsList}
</Field>
<button type="submit">Zatwierdz</button>
</Form>
</Formik>
</div>
);
};
const mapStateToProps = (state, props) => {
return {
movie: state.movie,
actors: state.actors,
};
};
const mapDispatchToProps = {
addActorToMovie,
};
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(PostForm)
);
The form works. Although instead of adding value to my actors array
. It creates a new object under movies array which looks like this:
actors: {actor: 'Some actor name'}
My reducers looks like this. Probably this is the source of the problem, but I don't really know how to do this.
export const movieReducer = (state = [], action) => {
switch (action.type) {
case "MOVIE_ADD":
return [...state, action.payload];
case "MOVIE_DELETE":
return [...state.filter((el) => el.id !== action.payload.id)];
case "MOVIEACTOR_ADD":
return {
...state,
actors: action.payload
};
default:
return state;
}
};
What I want to do is of course push the actor which i enter in my Formik
to an array of specified movie.
CodePudding user response:
Instead of declaring a new actors
property that is only the payload, you want to append it to the existing actors
array. However, you will first need to know which movie object you are updating, so this also needs to be sent in the action payload.
Update the submit handler to send both the movie id and the actor to be added.
const handleSubmit = ({ actor }) => {
addActorToMovie({
movieId: movie.id,
actor,
});
};
Update the reducer case to map the movies array, which appears to be state
in movieReducer
. Shallow copy the movies state
array, and for the matching movie id, shallow copy and append the actor to the actors array.
case "MOVIE_ACTOR_ADD":
const { actor, movieId } = action.payload;
return state.map(movie => movie.id === movieId
? {
...movie, // <-- shallow copy movie object
actors: [
...movie.actors, // <-- shallow copy existing array
actor // <-- append new actor payload
],
}
: movie
);
When adding a movie to state, ensure the actors
array property exists. If you aren't passing it the MOVIE_ADD
action payload then handle it in the reducer when adding a movie.
case "MOVIE_ADD":
return [
...state,
{ ...action.payload, actors: [] },
];