Reputation: 181
i am currently React SPA application based on react-redux. in the reducer.js, I have two main states, which is mylist and recommendations. I have attached two picture of actually applicaion. there are two pages. first one is Home page which there are three initial recommendations of movies. under each movie there is a add button, which once you clicked the botton and the expectation is selected movie will be removed and will be added to second pages which is a mylist page.
on the mylist page, there will be three initial movie. there is a remove button underneath each movie, once i have clicked remove button, expectation is the selected movie is removed and will be added back to home page.
rightnow, i have implemented the add button, which once i clicked add button, the selected movie will be removed and will be add to mylist page.
the problem is in the mylist page once i clicked remove. the selected movie is removed but its not added to the home page.
Home.js
class Home extends Component {
handleClick = id => {
this.props.addToList(id);
this.props.removeFromRecom(id);
};
render() {
let addedRecom1 = this.props.addedRecom.map(item => {
return (
<div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
<Image webP={item.img}></Image>
<p style={{ marginLeft: "44px" }}>{item.title}</p>
<button
to="/"
onClick={() => this.handleClick(item.id)}
style={{
marginLeft: "53px",
paddingLeft: "10px",
paddingRight: "10px",
paddingTop: "0px",
marginTop: "0px",
marginBottom: "20px"
}}
>
add
</button>
</div>
);
});
return (
<div>
<div>
<NavLink to="/myList" style={{ textDecoration: "none" }}>
<a
style={{
marginLeft: "378px",
fontFamily: "Arial Black",
color: "black",
border: "solid 1px",
backgroundColor: "orange",
paddingLeft: "35px",
paddingRight: "20px"
}}
>
<i
class="fa fa-plus"
style={{ paddingRight: "10px", paddingLeft: "5px" }}
></i>
My list
</a>
</NavLink>
<p style={{ marginTop: "20px", marginLeft: "20px" }}>
Recommendations
</p>
</div>
<div>
{this.props.recommendations.map(item => {
return (
<div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
<Image webP={item.img}></Image>
<p style={{ marginLeft: "44px" }}>{item.title}</p>
<button
to="/"
onClick={() => this.handleClick(item.id)}
style={{
marginLeft: "53px",
paddingLeft: "10px",
paddingRight: "10px",
paddingTop: "0px",
marginTop: "0px",
marginBottom: "20px"
}}
>
add
</button>
</div>
);
})}
</div>
{addedRecom1}
</div>
);
}
}
const mapStateToProps = state => {
return {
addedRecom: state.addedRecom,
recommendations: state.recommendations
};
};
const mapDispatchToProps = dispatch => {
return {
addToList: id => {
dispatch(addToList(id));
},
removeFromRecom: id => {
dispatch(removeFromRecom(id));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
MyList.js
class MyList extends Component {
handleRemoveMovie = id => {
this.props.removeFromList(id);
this.props.addToRecom(id);
};
render() {
let addedMovies = this.props.addedMovies.map(item => {
return (
<div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
<Image webP={item.img}></Image>
<p style={{ marginLeft: "44px" }}>{item.title}</p>
<button
to="myList"
onClick={() => {
this.handleRemoveMovie(item.id);
}}
style={{
marginLeft: "53px",
paddingLeft: "10px",
paddingRight: "10px",
paddingTop: "0px",
marginTop: "0px",
marginBottom: "20px"
}}
>
remove
</button>
</div>
);
});
return (
<div>
<div>
<NavLink to="/" style={{ textDecoration: "none" }}>
<a
style={{
marginLeft: "317px",
fontFamily: "Arial Black",
color: "black",
border: "solid 1px",
backgroundColor: "orange",
paddingLeft: "28px",
paddingRight: "20px"
}}
>
<i
class="fa fa-hand-o-left"
style={{ paddingRight: "10px", paddingLeft: "5px" }}
></i>
back to Home
</a>
</NavLink>
<p style={{ marginTop: "20px", marginLeft: "20px" }}>My Lists</p>
</div>
<div>
{this.props.mylist.map(item => {
return (
<div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
<Image webP={item.img}></Image>
<p style={{ marginLeft: "44px" }}>{item.title}</p>
<button
to="myList"
onClick={() => {
this.handleRemoveMovie(item.id);
}}
style={{
marginLeft: "53px",
paddingLeft: "10px",
paddingRight: "10px",
paddingTop: "0px",
marginTop: "0px",
marginBottom: "20px"
}}
>
remove
</button>
</div>
);
})}
</div>
{addedMovies}
</div>
);
}
}
const mapStateToProps = state => {
return {
addedMovies: state.addedMovies,
mylist: state.mylist
};
};
const mapDispatchToProps = dispatch => {
return {
removeFromList: id => {
dispatch(removeFromList(id));
},
addToRecom: id => {
dispatch(addToRecom(id));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MyList);
reducer.js
const initialState = {
mylist: [
{
title: "Futurama",
id: 1,
img: "http://cdn1.nflximg.net/webp/7621/3787621.webp"
},
{
title: "The Interview",
id: 2,
img: "http://cdn1.nflximg.net/webp/1381/11971381.webp"
},
{
title: "Gilmore Girls",
id: 3,
img: "http://cdn1.nflximg.net/webp/7451/11317451.webp"
}
],
recommendations: [
{
title: "Family Guy",
id: 4,
img: "http://cdn5.nflximg.net/webp/5815/2515815.webp"
},
{
title: "The Croods",
id: 5,
img: "http://cdn3.nflximg.net/webp/2353/3862353.webp"
},
{
title: "Friends",
id: 6,
img: "http://cdn0.nflximg.net/webp/3200/9163200.webp"
}
],
addedMovies: [],
addedRecom: []
};
const reducer = (state = initialState, action) => {
if (action.type === REMOVE_FROM_RECOM) {
let Recom = state.recommendations.filter(item => item.id !== action.id);
return {
...state,
recommendations: Recom
};
}
if (action.type === ADD_TO_LIST) {
let addedMovie = state.recommendations.find(item => item.id === action.id);
let existed_movie = state.mylist;
if (existed_movie) {
return {
...state,
addedMovies: [...state.addedMovies, addedMovie]
};
}
}
if (action.type === ADD_TO_RECOM) {
let add_recom = state.mylist.find(item => item.id === action.id);
let current_recom = state.recommendations;
if (current_recom) {
return {
...state,
addedRecom: [...state.addedRecom, add_recom]
};
}
}
if (action.type === REMOVE_FROM_LIST) {
let removedMovie = state.mylist.find(item => item.id === action.id);
let newMovie = state.addedMovies.filter(item => item.id !== action.id);
return {
...state,
addedMovies: newMovie
};
} else {
return state;
}
};[![enter image description here][1]][1]
export default reducer;
TypeError: Cannot read property 'id' of undefined
(anonymous function)
src/Home.js:17
14 | render() {
15 | let addedRecom1 = this.props.addedRecom.map(item => {
16 | return (
> 17 | <div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
| ^ 18 | <Image webP={item.img}></Image>
19 | <p style={{ marginLeft: "44px" }}>{item.title}</p>
20 | <button
[![enter image description here][1]][1]
Upvotes: 0
Views: 1382
Reputation: 1568
In general you should have only two lists, one for recommendations
and one for myList
, and two reducers that handle deletion and addition of items from myList
:
const initialState = {
myList: [
{
title: 'Futurama',
id: 1,
img: 'http://cdn1.nflximg.net/webp/7621/3787621.webp'
},
{
title: 'The Interview',
id: 2,
img: 'http://cdn1.nflximg.net/webp/1381/11971381.webp'
},
{
title: 'Gilmore Girls',
id: 3,
img: 'http://cdn1.nflximg.net/webp/7451/11317451.webp'
}
],
recommendations: [
{
title: 'Family Guy',
id: 4,
img: 'http://cdn5.nflximg.net/webp/5815/2515815.webp'
},
{
title: 'The Croods',
id: 5,
img: 'http://cdn3.nflximg.net/webp/2353/3862353.webp'
},
{
title: 'Friends',
id: 6,
img: 'http://cdn0.nflximg.net/webp/3200/9163200.webp'
}
]
};
Each reducer is incharge of updating both recommendations
and myList
upon deletion/addition of a movie:
const reducer = (state = initialState, action) => {
if (action.type === 'ADD_TO_LIST') {
let addToMyList = state.recommendations.find(item => item.id === action.id);
return {
...state,
myList: [ ...state.myList, addToMyList ],
recommendations: state.recommendations.filter(item => item.id !== action.id)
};
}
if (action.type === 'REMOVE_FROM_LIST') {
let removedItem = state.myList.find(item => item.id === action.id);
return {
...state,
myList: state.myList.filter(item => item.id !== action.id),
recommendations: [ ...state.recommendations, removedItem ]
};
} else {
return state;
}
};
export default reducer;
Upvotes: 1