I'm trying to update value in react functional compoenent through input element but after the first value I'm unable to type
My Code:
import React from "react";
import "./App.css";
const { useState } = React;
function App() {
const [items, setItems] = useState([
{ value: "" },
{ value: "" },
{ value: "" },
]);
const [count, setCount] = useState(0);
const Item = React.memo(({ id, value, onChange }) => {
return (
<div className="item">Item
<input
onChange={(e) => onChange(id, e.target.value)}
value={value}
/>
</div>
);
});
return (
<div className="app">
<h1>Parent</h1>
<p style={{marginTop: '20px'}}>Holds state: {count}, Does't passes to it items</p>
<p style={{marginTop: '20px'}}>{JSON.stringify(items)}</p>
<button onClick={() => setCount((prev) => prev 1)} style={{marginTop: '20px'}}>
Update Parent
</button>
<ul className="items" style={{marginTop: '20px'}}>
{items.map((item, index) => {
return (
<Item
key={index}
id={index}
value={item.value}
onChange={(id, value) =>
setItems(
items.map((item, index) => {
return index !== id
? item
: { value: value };
})
)
}
/>
);
})}
</ul>
</div>
);
}
export default App;
CodePudding user response:
You should move the Item
declaration outside the App
component. Having a component declaration inside another one is almost always a bad idea. Explanation below.
import React, { useState } from "react";
const Item = React.memo(({ id, value, onChange }) => {
return (
<div className="item">
Item
<input onChange={(e) => onChange(id, e.target.value)} value={value} />
</div>
);
});
function App() {
const [items, setItems] = useState([
{ value: "" },
{ value: "" },
{ value: "" }
]);
const [count, setCount] = useState(0);
return (
<div className="app">
<h1>Parent</h1>
<p style={{ marginTop: "20px" }}>
Holds state: {count}, Does't passes to it items
</p>
<p style={{ marginTop: "20px" }}>{JSON.stringify(items)}</p>
<button
onClick={() => setCount((prev) => prev 1)}
style={{ marginTop: "20px" }}
>
Update Parent
</button>
<ul className="items" style={{ marginTop: "20px" }}>
{items.map((item, index) => {
return (
<Item
key={index}
id={index}
value={item.value}
onChange={(id, value) =>
setItems(
items.map((item, index) => {
return index !== id ? item : { value: value };
})
)
}
/>
);
})}
</ul>
</div>
);
}
export default App;
When a component definition is inside another component, React will re-declare the inner component every time the parent re-renders. This means, that any state held by the inner component will be lost.
In your case, since every time there is an entirely new component, the input was not the same input as in the previous render. This means that the input
that was in focus in the previous render is not present anymore, and the new input is not focused anymore.
You should also probably change
setItems(
items.map((item, index) => {
return index !== id ? item : { value: value };
})
)
to
prev.map((item, index) => {
return index !== id ? item : { value: value };
})
)
It's a good idea to use the function notation for set state when the new state depends on the old state value.