I have an object in a React state:
{
name: "",
price: 0.0,
components: [],
}
and I am trying to modify its components
property. I can see in the debugger that the state is updated but the rendering is not updated, so the different nested objects are not rendered.
I guess this is very similar to what is described here:
But I have the feeling I am doing what is precognised, using an updater function, but it doens't work.
Here is the code:
import React, { useState } from "react";
import "./styles.css";
defaultComponent = {
name: "C1",
comment: "",
}
export default function App() {
const [system, setSystem] = useState({
name: "",
price: 0.0,
components: [],
})
function modifySystemProperty(property, newValue) {
let modifiedSystem = { ...system };
modifiedSystem[property] = newValue;
setSystem(modifiedSystem);
}
function addComponent() {
setSystem((system) => ({
...system,
components: [...system.components, defaultComponent],
}));
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h3>Edit system:</h3>
<form>
<div>
<label htmlFor="form-name">Name: </label>
<input
id="form-name"
type="text"
value={system?.name}
onChange={(e) => modifySystemProperty("name", e.target.value)}
/>
</div>
<div>
<label htmlFor="form-price">Price: </label>
<input
id="form-price"
type="number"
min="0"
step="0.1"
value={system?.price}
onChange={(e) => modifySystemProperty("price", +e.target.value)}
/>
</div>
<div>
<p>Components:</p>
<ul>
{system.components.map((c, idx) => {
<li key={idx}>Component: {c.name}</li>
})}
</ul>
<button
onClick={() => addComponent()}
>
+ Add Component
</button>
</div>
</form>
</div>
);
}
And running on CodeSandBox here: https://codesandbox.io/p/sandbox/thirsty-paper-jzsjfy Can you tell me the proper way of updating this object property? Thank you.
I see at least two issues:
type="submit"
. You need to change it to <button type="button">
.system.components.map()
loop doesn't render anything. You can either (i) add a return
statement inside the loop body or (ii) change the braces {}
to parentheses ()
. e.g.{system.components.map((c, idx) => (
<li key={idx}>Component: {c.name}</li>
))}
Thirdly, I think adding the same defaultComponent
to the array every time will create issues once you start editing components, since you're reusing the same object. I suggest adding a new object each time. i.e.
function addComponent() {
setSystem((system) => ({
...system,
components: [...system.components, {
name: "C1",
comment: "",
}],
}));
};