My reducer by default returns this (it's from a tutorial):
return [
{
id: 1,
first: "Bucky",
last: "Roberts",
age: 71,
description: "Bucky is a React developer and YouTuber",
thumbnail: ".jpg"
},
{
id: 2,
first: "Joby",
last: "Wasilenko",
age: 27,
description: "Joby loves the Packers, cheese, and turtles.",
thumbnail: ".png"
},
{
id: 3,
first: "Madison",
last: "Williams",
age: 24,
description: "Madi likes her dog but it is really annoying.",
thumbnail: ".png"
}
]
}
I now want to be able to delete user entries from this 'list'. So I went ahead and built a delete user action creator etc., so in addition to the above data, there is now this code preceding it:
switch (action.type) {
case 'USER_DELETED':
console.log("Hello from reducer-users");
let newState = Object.assign({}, state, );
// console.log(newState == state);
// console.log(newState.users == state.users)
// delete newState.users;
return newState;
break;
}
The idea is that if someone clicks 'DELETE USER', this function is called and a new state is returned with the appropriate user deleted. It also works in the sense that the function is being called (the console.log is displayed). However, I am not sure how to go about deleting a user from this list now? I understand that I need to copy the current state and then delete the user from the copy, & then return the copy so it bees the new state. Is this correct? If so, I do not know how exactly to dothis. If I try to execute what I have written above, which was just something s.b. on stackoverflow wrote, I get an error:
user-list.js?1cc2:11 Uncaught TypeError: this.props.users.map is not a function(…)renderList @ user-list.js?1cc2:11render @ ?d41d:33(anonymous function) @ ReactCompositeComponent.js?cd59:793measureLifeCyclePerf @ ReactCompositeComponent.js?cd59:74_renderValidatedComponentWithoutOwnerOrContext @ ReactCompositeComponent.js?cd59:792_renderValidatedComponent @ ReactCompositeComponent.js?cd59:819_updateRenderedComponent @ ReactCompositeComponent.js?cd59:743_performComponentUpdate @ ReactCompositeComponent.js?cd59:721updateComponent @ ReactCompositeComponent.js?cd59:642receiveComponent @ ReactCompositeComponent.js?cd59:544receiveComponent @ ReactReconciler.js?6bfa:126_updateRenderedComponent @ ReactCompositeComponent.js?cd59:751_performComponentUpdate @ ReactCompositeComponent.js?cd59:721updateComponent @ ReactCompositeComponent.js?cd59:642performUpdateIfNecessary @ ReactCompositeComponent.js?cd59:558performUpdateIfNecessary @ ReactReconciler.js?6bfa:158runBatchedUpdates @ ReactUpdates.js?ce09:151perform @ Transaction.js?6dff:138perform @ Transaction.js?6dff:138perform @ ReactUpdates.js?ce09:90flushBatchedUpdates @ ReactUpdates.js?ce09:173closeAll @ Transaction.js?6dff:204perform @ Transaction.js?6dff:151batchedUpdates @ ReactDefaultBatchingStrategy.js?ef70:63batchedUpdates @ ReactUpdates.js?ce09:98dispatchEvent @ ReactEventListener.js?2365:150
This is the whole reducers file:
/*
* The users reducer will always return an array of users no matter what
* You need to return something, so if there are no users then just return an empty array
* */
export default function (state = null, action) {
switch (action.type) {
case 'USER_DELETED':
console.log("Hello from reducer-users");
//let newState = Object.assign([{}], state);
// return newState;
return state.filter(user => user.id !== action.userIdToDelete);
console.log("UNTIL HERE");
// console.log(newState.users == state.users)
// delete newState.users;
// return ([{
// id: 5,
// first: "Bucky",
// last: "Roberts",
// age: 71,
// description: "Bucky is a React developer and YouTuber",
// thumbnail: ".jpg"
// }]);
break;
console.log("UNTIL HERE2222");
}
return [
{
id: 1,
first: "Bucky",
last: "Roberts",
age: 71,
description: "Bucky is a React developer and YouTuber",
thumbnail: ".jpg"
},
{
id: 2,
first: "Joby",
last: "Wasilenko",
age: 27,
description: "Joby loves the Packers, cheese, and turtles.",
thumbnail: ".png"
},
{
id: 3,
first: "Madison",
last: "Williams",
age: 24,
description: "Madi likes her dog but it is really annoying.",
thumbnail: ".png"
}
]
}
My reducer by default returns this (it's from a tutorial):
return [
{
id: 1,
first: "Bucky",
last: "Roberts",
age: 71,
description: "Bucky is a React developer and YouTuber",
thumbnail: "http://i.imgur./7yUvePI.jpg"
},
{
id: 2,
first: "Joby",
last: "Wasilenko",
age: 27,
description: "Joby loves the Packers, cheese, and turtles.",
thumbnail: "http://i.imgur./52xRlm8.png"
},
{
id: 3,
first: "Madison",
last: "Williams",
age: 24,
description: "Madi likes her dog but it is really annoying.",
thumbnail: "http://i.imgur./4EMtxHB.png"
}
]
}
I now want to be able to delete user entries from this 'list'. So I went ahead and built a delete user action creator etc., so in addition to the above data, there is now this code preceding it:
switch (action.type) {
case 'USER_DELETED':
console.log("Hello from reducer-users");
let newState = Object.assign({}, state, );
// console.log(newState == state);
// console.log(newState.users == state.users)
// delete newState.users;
return newState;
break;
}
The idea is that if someone clicks 'DELETE USER', this function is called and a new state is returned with the appropriate user deleted. It also works in the sense that the function is being called (the console.log is displayed). However, I am not sure how to go about deleting a user from this list now? I understand that I need to copy the current state and then delete the user from the copy, & then return the copy so it bees the new state. Is this correct? If so, I do not know how exactly to dothis. If I try to execute what I have written above, which was just something s.b. on stackoverflow wrote, I get an error:
user-list.js?1cc2:11 Uncaught TypeError: this.props.users.map is not a function(…)renderList @ user-list.js?1cc2:11render @ ?d41d:33(anonymous function) @ ReactCompositeComponent.js?cd59:793measureLifeCyclePerf @ ReactCompositeComponent.js?cd59:74_renderValidatedComponentWithoutOwnerOrContext @ ReactCompositeComponent.js?cd59:792_renderValidatedComponent @ ReactCompositeComponent.js?cd59:819_updateRenderedComponent @ ReactCompositeComponent.js?cd59:743_performComponentUpdate @ ReactCompositeComponent.js?cd59:721updateComponent @ ReactCompositeComponent.js?cd59:642receiveComponent @ ReactCompositeComponent.js?cd59:544receiveComponent @ ReactReconciler.js?6bfa:126_updateRenderedComponent @ ReactCompositeComponent.js?cd59:751_performComponentUpdate @ ReactCompositeComponent.js?cd59:721updateComponent @ ReactCompositeComponent.js?cd59:642performUpdateIfNecessary @ ReactCompositeComponent.js?cd59:558performUpdateIfNecessary @ ReactReconciler.js?6bfa:158runBatchedUpdates @ ReactUpdates.js?ce09:151perform @ Transaction.js?6dff:138perform @ Transaction.js?6dff:138perform @ ReactUpdates.js?ce09:90flushBatchedUpdates @ ReactUpdates.js?ce09:173closeAll @ Transaction.js?6dff:204perform @ Transaction.js?6dff:151batchedUpdates @ ReactDefaultBatchingStrategy.js?ef70:63batchedUpdates @ ReactUpdates.js?ce09:98dispatchEvent @ ReactEventListener.js?2365:150
This is the whole reducers file:
/*
* The users reducer will always return an array of users no matter what
* You need to return something, so if there are no users then just return an empty array
* */
export default function (state = null, action) {
switch (action.type) {
case 'USER_DELETED':
console.log("Hello from reducer-users");
//let newState = Object.assign([{}], state);
// return newState;
return state.filter(user => user.id !== action.userIdToDelete);
console.log("UNTIL HERE");
// console.log(newState.users == state.users)
// delete newState.users;
// return ([{
// id: 5,
// first: "Bucky",
// last: "Roberts",
// age: 71,
// description: "Bucky is a React developer and YouTuber",
// thumbnail: "http://i.imgur./7yUvePI.jpg"
// }]);
break;
console.log("UNTIL HERE2222");
}
return [
{
id: 1,
first: "Bucky",
last: "Roberts",
age: 71,
description: "Bucky is a React developer and YouTuber",
thumbnail: "http://i.imgur./7yUvePI.jpg"
},
{
id: 2,
first: "Joby",
last: "Wasilenko",
age: 27,
description: "Joby loves the Packers, cheese, and turtles.",
thumbnail: "http://i.imgur./52xRlm8.png"
},
{
id: 3,
first: "Madison",
last: "Williams",
age: 24,
description: "Madi likes her dog but it is really annoying.",
thumbnail: "http://i.imgur./4EMtxHB.png"
}
]
}
Share
Improve this question
edited Oct 26, 2016 at 14:19
Peter Zacharias
asked Oct 26, 2016 at 12:14
Peter ZachariasPeter Zacharias
7954 gold badges13 silver badges24 bronze badges
6
- 1 Totally unrelated, but the action name seems wrong-there "-ed" at the end implies past tense, which isn't what's happening. This action is what causes a user to be deleted, after which a "user deleted" action could be fired. – Dave Newton Commented Oct 26, 2016 at 13:17
- thanks, if it's better practice to keep it present tense, i will go ahead and change this. thank you. – Peter Zacharias Commented Oct 26, 2016 at 13:55
- It's just basic grammar, e.g., "DELETE_USER" then "USER_DELETED". It's not a big deal, just pointing it out. – Dave Newton Commented Oct 26, 2016 at 14:17
- I understood that. I am just really happy that you point that out, as I want to develop good coding habits – Peter Zacharias Commented Oct 26, 2016 at 14:18
- Naming is hard: martinfowler./bliki/TwoHardThings.html, slideshare/pirhilton/… – Dave Newton Commented Oct 26, 2016 at 14:20
1 Answer
Reset to default 6You're correct in saying that your reducer should return a new copy of the state, without mutating the current one. This approach have a few very nice benefits (see here: http://redux.js/docs/Glossary.html#reducer)
Assuming the state managed by this reducer is an array, to delete a user from your list, you can do the following:
switch (action.type) {
case 'USER_DELETED':
return state.filter(user => user.id !== action.userIdToDelete);
}
The key point is that Array.filter returns a new array applying the callback to each element, deciding which one to keep/filter, it doesn't mutate the original array. Check out the docs here https://developer.mozilla/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
You could be tempted to use Array.splice instead (https://developer.mozilla/en/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) but that would be wrong in this case, since splice
"changes the content of an array by removing existing elements and/or adding new elements.", so it does mutate the original one.
Note that you need to pass userIdToDelete
in your action payload.
Also, you don't need break
as you're already using return
.
As for the exact reason to the error you posted, the problem is that at the moment your reducer is returning an object instead of an array: let newState = Object.assign({}, state, );
Then, somewhere in your ponents you're reading from the state and you're trying to call map
on your state, which is not an array anymore (hence no map
method defined for this.props.users
Another suggestion:
I'd really remend you to start using plain Js inside your reducers, always being careful about methods that mutate objects in place. After you know what you're doing, sometimes in Js working with immutable data structures can be clumsy, so you can start looking into using these libraries to ease the pain for more plicated scenarios (but only if you really need), ordered by plexity (obviously in my opinion):
- https://facebook.github.io/react/docs/update.html
- https://github./substantial/updeep
- https://facebook.github.io/immutable-js/