Reputation: 228
I'm trying to create a component for my app, where when I click on a button input field opens, after I add text I click on that button again and another input opens up, and so on. Then e.target.value of all those inputs should be saved in a different state and displayed in another components. So far I was able to create such input, but I can't figure out how to edit input fields. My code:
import "./styles.css";
import React, { useState } from "react";
export default function App() {
const [input, setInput] = useState([{}]);
const [data, setData] = useState([]);
function handleChange(i, e) {
e.preventDefault();
setInput([
{
id: i,
value: e.target.value
}
]);
}
function handleAddInput() {
const values = [...input];
values.push({ value: null });
setInput(values);
}
const handleSave = () => {
let value = input?.map((item) => {
return item.value;
});
if (!value || /^\s*$/.test(value)) {
return;
}
const newData = [...data, ...input];
setData(newData);
setInput([])
};
return (
<div className="App">
<button type="button" className="btn" onClick={() => handleAddInput()}>
Add Input fields
</button>
{input?.map((input, idx) => {
return (
<div key={input.id}>
<input
type="text"
placeholder="Enter text"
onChange={(e) => handleChange(idx, e)}
value={input.value}
/>
</div>
);
})}
<h2>Display Added Fields and Edit</h2>
{data?.map((item) => {
return (
<>
<input defaultValue={item.value}
/>
</>
);
})}
<button className="btn" onClick={handleSave}>Save</button>
{data?.map((item) => {
return (
<div style={{ display: "flex", flexDorection: "column", marginTop:"20px" }}>
{item.value}
</div>
);
})}
</div>
);
}
At the moment when I click "Add Input fields" new input pops up, I enter any input text, then I click Add Input fields again and another input opens up, when I click "Save" button, all input values are saved to state (as data) and displayed as inputs, and after I can map through "data" and display received inputs. Like on image I added "first", then "second" and they're getting displayed fine, but I don't know how I could edit them, for example change "first" to "third" and on Save button "third" should be displayed instead of "first". Any help and suggestions are greatly appreciated.
Upvotes: 1
Views: 10906
Reputation: 203466
handleChange
doesn't persist existing input state.input
to data
id
property, it is trivial to update by index.onSubmit
handler to handle updating the data
state array values.Code:
function App() {
const [input, setInput] = useState([]);
const [data, setData] = useState([]);
function handleChange(i, e) {
e.preventDefault();
setInput((values) =>
values.map((value, index) => (index === i ? e.target.value : value))
);
}
function handleAddInput() {
setInput((input) => input.concat(""));
}
const saveHandler = (e) => {
e.preventDefault();
// Map all existing form field values
setData((data) => data.map((_, i) => e.target[i].value));
// If there are any input values, add these and clear inputs
if (input.length) {
setData((data) => data.concat(input));
setInput([]);
}
};
return (
<div className="App">
<button type="button" className="btn" onClick={handleAddInput}>
Add Input fields
</button>
{input.map((input, idx) => {
return (
<div key={idx}>
<input
type="text"
placeholder="Enter text"
onChange={(e) => handleChange(idx, e)}
value={input.value}
/>
</div>
);
})}
<h2>Display Added Fields and Edit</h2>
<form onSubmit={saveHandler}>
{data.map((item, i) => {
return (
<div key={i}>
<input id={i} defaultValue={item} />
</div>
);
})}
<button className="btn" type="submit">
Save
</button>
</form>
{data.map((item, i) => {
return (
<div
key={i}
style={{
display: "flex",
flexDirection: "column",
marginTop: "20px"
}}
>
{item}
</div>
);
})}
</div>
);
}
Edit to generate GUIDs and persist id
property through to the data
state.
id
for matching elements for any value updates.input
array to data
when saving the input fields.id
at every step so it's clear the data elements are still the same objects.Code:
function App() {
const [input, setInput] = useState([]);
const [data, setData] = useState([]);
const handleChange = (id) => (e) => {
e.preventDefault();
setInput((values) =>
values.map((el) =>
el.id === id
? {
...el,
value: e.target.value
}
: el
)
);
};
function handleAddInput() {
setInput((input) =>
input.concat({
id: uuidV4(),
value: ""
})
);
}
const saveHandler = (e) => {
e.preventDefault();
setData((data) =>
data.map((el) => ({
...el,
value: e.target[el.id].value
}))
);
if (input.length) {
setData((data) => data.concat(input));
setInput([]);
}
};
return (
<div className="App">
<button type="button" className="btn" onClick={handleAddInput}>
Add Input fields
</button>
{input.map((input) => {
return (
<label key={input.id}>
{input.id}
<input
type="text"
placeholder="Enter text"
onChange={handleChange(input.id)}
value={input.value}
/>
</label>
);
})}
<h2>Display Added Fields and Edit</h2>
<form onSubmit={saveHandler}>
{data.map((item) => {
return (
<div key={item.id}>
<label>
{item.id}
<input id={item.id} defaultValue={item.value} />
</label>
</div>
);
})}
<button className="btn" type="submit">
Save
</button>
</form>
{data.map((item) => {
return (
<div
key={item.id}
style={{
display: "flex",
flexDirection: "column",
marginTop: "20px"
}}
>
<div>
{item.id} - {item.value}
</div>
</div>
);
})}
</div>
);
}
Upvotes: 1