Reputation: 160
I've got a simple problem here with creating dynamic keys for a state object in React using computed property names
, but after trying for hours I can't figure out how to fix it.
See the comments in the code for details, and a short overview below.
Scenario: a user clicks on an option inside a given page and the updateSelection
method below is called. This method updates the state with new information.
Problem: Using computed property names
I think(?) makes the keys be unique inside of an object, and any future keys of the same name are replaced. In some cases, I want to allow multiple selections of the same page, this is the part that I can't figure out. I'm open to turning my state into an array of objects if that works better.
state = {
userSelection: {}
}
const updateSelection = page => {
const { pageName, category, multiple } = page;
const selection = {
pageName,
category
}
// If `multiple` is false, pages must be unique in the state object.
// This currently works fine.
if (!multiple) {
this.setState(prevState => ({
userSelection: {
...prevState.userSelection,
[pageName]: selection
}
}));
// New state for userSelection is now this:
// {
// Page 1: {pageName: "Page 1", category: X},
// Page 2: {pageName: "Page 2", category: Y}
// }
return;
}
// If `multiple` is true for a page, the same page key can be added to
// the state object as long as the category is different. If the category
// is the same, the page is replaced just like when `multiple` is false.
// The desired result for userSelection would look something like this:
// {
// Page 1: {pageName: "Page 1", category: X},
// Page 2: {pageName: "Page 2", category: Y},
// Page 2: {pageName: "Page 2", category: Z},
// Page 2: {pageName: "Page 2", category: W, other unique props}
// }
}
Update: for deleting a page, the input would be the pageName
which could be passed as an argument to a deletePage
method similar to updateSelection
.
Upvotes: 2
Views: 603
Reputation: 22324
If you wish to use an array for multiple values of each page, like this:
{
"Page 1": [ {pageName: "Page 1", category: X} ],
"Page 2": [
{pageName: "Page 2", category: Y},
{pageName: "Page 2", category: Z},
{pageName: "Page 2", category: W, other unique props}
]
}
then you can adapt following code for adding/replacing items in the arrays:
function updateSelection ({pageName, category, multiple}) {
const selection = {pageName, category} // assuming more props here
if (!multiple) {} // TODO keep old code or change to arrays too
this.setState(prevState => {
const {userSelection} = prevState
if (!userSelection.hasOwnProperty(pageName)) {
userSelection[pageName] = []
}
let categoryWasReplaced = false
const nextUserSelection = {
...userSelection,
[pageName]: userSelection[pageName].map(item => {
// TODO use .filter() if needed to remove from selection
if (item.category === category) {
categoryWasReplaced = true
return selection
}
return item
})
}
if (!categoryWasReplaced) {
nextUserSelection[pageName].push(selection)
}
return {userSelection: nextUserSelection}
})
}
// ### test ###
let state = {userSelection: {}}
const mockSetState = f => {
state = {...state, ...f(state)}
}
const testUpdateSelection = updateSelection.bind({state, setState: mockSetState})
testUpdateSelection({pageName: 'Page 1', category: 'X', multiple: true})
testUpdateSelection({pageName: 'Page 2', category: 'Y', multiple: true})
testUpdateSelection({pageName: 'Page 2', category: 'Y', multiple: true})
testUpdateSelection({pageName: 'Page 2', category: 'Y', multiple: true})
testUpdateSelection({pageName: 'Page 2', category: 'Z', multiple: true})
testUpdateSelection({pageName: 'Page 2', category: 'W', multiple: true})
console.log(state)
Upvotes: 1