Complex State Help / Best Practice

So I am making a crafting calculator for a game where each item costs different amounts of things to create, and some of them have overlap. For example, a rocket could take the following resources and components to create.
cost: {
resources: {
sulfur: 1400,
charcoal: 1950,
frags: 100,
low_grade: 30,
},
components: {
pipes: 2,
}
}
cost: {
resources: {
sulfur: 1400,
charcoal: 1950,
frags: 100,
low_grade: 30,
},
components: {
pipes: 2,
}
}
And a C4 could require the following.
cost: {
resources: {
sulfur: 2200,
charcoal: 3000,
frags: 200,
low_grade: 60,
cloth: 5,
},
components: {
tech_trash: 2,
}
}
cost: {
resources: {
sulfur: 2200,
charcoal: 3000,
frags: 200,
low_grade: 60,
cloth: 5,
},
components: {
tech_trash: 2,
}
}
My UI allows for 3 ways to change the number of any item they are using in the calculator (see screenshot). They can manually enter a number, subtract one, or add one. I am running into some confusion on how to update this state. Each "card" (screenshot area) has an amount state which tracks the number in the input box. This amount is going to determine the calculations. Its tricky when they combine different items like the rocket and C4 example I sent. When they type in the input I have an onChange event handler, when they click either of the buttons I am changing the amount state. A little bit stuck and would appreciate any advice for best practices here. Thanks I have an idea for a really ugly way but I am confident its not correct
2 Replies
ahmadaccino
ahmadaccino17mo ago
This is something I encountered quite frequently. It can definitely get hectic with a complex object. Sometimes you have step back and think of a more SOLID approach and ask yourself if you can optimize the way the dataflow is set up to simplify state management. If your react components are split up enough, the state that they do own will be minimal. But, sometimes, especially for mutations, one needs to go ahead and deal with a deeply nested object in state. If thats the case, then the spread operator is you best friend, as you dont need to enter values that will remain the same. That way the code will only show the values that are getting updated. Make sure to use the spread operator BEFORE the fields that you are overriding, otherwise your override will get overridden by the previous object. On an unrelated note, it might make more sense for resources and components to be arrays. If they are a list of resources and the keys aren't always the same list, an array should be used. Nonetheless, here are some examples (I added an example with lodash incase you dont care about type safety):
const [costObj, setCostObj] = useState({
cost: {
resources: {
sulfur: 2200,
charcoal: 3000,
frags: 200,
low_grade: 60,
cloth: 5,
},
components: {
tech_trash: 2,
},
},
});

// Switch Sulfur from 2200 -> 5000

setCostObj({
cost: {
...costObj.cost,
resources: {
...costObj.cost.resources,
sulfur: 5000,
},
},
});

// Increment Sulfur by 1

setCostObj({
cost: {
...costObj.cost,
resources: {
...costObj.cost.resources,
sulfur: costObj.cost.resources.sulfur + 1,
},
},
});

// Same as Previous, but dangerously using lodash
const originalSulfurValue = get(costObj, 'cost.resources.sulfur')
const newCostObj = costObj
set(newCostObj, 'cost.resources.sulfur', originalSulfurValue + 1)
setCostObj(costObj)
const [costObj, setCostObj] = useState({
cost: {
resources: {
sulfur: 2200,
charcoal: 3000,
frags: 200,
low_grade: 60,
cloth: 5,
},
components: {
tech_trash: 2,
},
},
});

// Switch Sulfur from 2200 -> 5000

setCostObj({
cost: {
...costObj.cost,
resources: {
...costObj.cost.resources,
sulfur: 5000,
},
},
});

// Increment Sulfur by 1

setCostObj({
cost: {
...costObj.cost,
resources: {
...costObj.cost.resources,
sulfur: costObj.cost.resources.sulfur + 1,
},
},
});

// Same as Previous, but dangerously using lodash
const originalSulfurValue = get(costObj, 'cost.resources.sulfur')
const newCostObj = costObj
set(newCostObj, 'cost.resources.sulfur', originalSulfurValue + 1)
setCostObj(costObj)
Unknown User
Unknown User17mo ago
Message Not Public
Sign In & Join Server To View