Save values in array of data - javascript

My react application take data from user and should output them when user click on SUBMIT button.
Now user open the application appears a number input. There he can set a number, for example 2. After that appears 2 boxes, where user can add fields cliking on the button +. In each field user have to add his name and last name. Now the application works, but no in the right way.
Now, if user add his user name and last name in first box and click on SUBMIT, everithing appears in:
const onFinish = values => {
const res = {
data: [dataAll]
};
console.log("Received values of form:", res);
};
But if user add one another inputs clicking on add fields button, the first data dissapears. The same issue is when user add data in 2 boxes, the data is saved just from the last box.
function handleInputChange(value) {
const newArray = Array.from({ length: value }, (_, index) => index + 1);
setNr(newArray);
}
const onFinish = values => {
const res = {
data: [dataAll]
};
console.log("Received values of form:", res);
};
return (
<div>
<Form name="outter" onFinish={onFinish}>
{nr.map(i => (
<div>
<p key={i}>{i}</p>
<Child setDataAll={setDataAll} nr={i} />
</div>
))}
<Form.Item
name={["user", "nr"]}
label="Nr"
rules={[{ type: "number", min: 0, max: 7 }]}
>
<InputNumber onChange={handleInputChange} />
</Form.Item>
<Form.Item>
<Button htmlType="submit" type="primary">
Submit
</Button>
</Form.Item>
</Form>
</div>
);
};
Mu expected result after clicking on submit is:
data:[
{
nr:1,
user: [
{
firstName:'John',
lastName: 'Doe'
},
{
firstName:'Bill',
lastName: 'White'
}
]
},
{
nr:2,
user: [
{
firstName:'Carl',
lastName: 'Doe'
},
{
firstName:'Mike',
lastName: 'Green'
},
{
firstName:'Sarah',
lastName: 'Doe'
}
]
},
]
....
// object depend by how many fields user added in each nr
Question: How to achieve the above result?
demo: https://codesandbox.io/s/wandering-wind-3xgm7?file=/src/Main.js:288-1158

Here's an example how you could do it.
As you can see Child became Presentational Component and only shows data.
Rest of logic went to the Parent component.
const { useState, useEffect, Fragment } = React;
const Child = ({nr, user, onAddField, onChange}) => {
return <div>
<div>{nr}</div>
{user.map(({firstName, lastName}, index) => <div key={index}>
<input onChange={({target: {name, value}}) => onChange(nr, index, name, value)} value={firstName} name="firstName" type="text"/>
<input onChange={({target: {name, value}}) => onChange(nr, index, name, value)} value={lastName} name="lastName" type="text"/>
</div>)}
<button onClick={() => onAddField(nr)}>Add Field</button>
</div>
}
const App = () => {
const [items, setItems] = useState([
{
nr: 1,
user: []
},
{
nr: 2,
user: [
{firstName: 'test1', lastName: 'test1'},
{firstName: 'test2', lastName: 'test2'}
]
}
]);
const onCountChange = ({target: {value}}) => {
setItems(items => {
const computedList = Array(Number(value)).fill(0).map((pr, index) => ({
nr: index + 1,
user: []
}))
const merged = computedList.reduce((acc, value) => {
const item = items.find(pr => pr.nr === value.nr) || value;
acc = [...acc, item];
return acc;
}, [])
return merged;
})
}
const onChildChange = (nr, index, name, value) => {
setItems(items => {
const newItems = [...items];
const item = newItems.find(pr => pr.nr === nr);
const field = item.user.find((pr, ind) => index === ind)
field[name] = value;
return newItems;
});
}
const onAddField = (nr) => {
setItems(items => {
const newItems = [...items];
const item = newItems.find(pr => pr.nr === nr);
item.user = [...item.user, {
firstName: '',
lastName: ''
}];
return newItems;
})
}
const onClick = () => {
console.log({data: items});
}
return <div>
{items.map((pr, index) => <Child {...pr} onAddField={onAddField} onChange={onChildChange} key={index} />)}
<input onChange={onCountChange} value={items.length} type="number"/>
<button onClick={onClick}>Submit</button>
</div>
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>

Related

When using a UseState with an array, React doesn't update my component render on screen after using setTimeout [duplicate]

I have retrieved data stored using useState in an array of object, the data was then outputted into form fields. And now I want to be able to update the fields (state) as I type.
I have seen examples on people updating the state for property in array, but never for state in an array of object, so I don't know how to do it. I've got the index of the object passed to the callback function but I didn't know how to update the state using it.
// sample data structure
const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [data, setData] = useState([]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
setData() // ??
}
return (
<React.Fragment>
{data.map((data, index) => {
<li key={data.name}>
<input type="text" name="name" value={data.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
Here is how you do it:
// sample data structure
/* const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
] */ // make sure to set the default value in the useState call (I already fixed it)
const [data, setData] = useState([
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
let newArr = [...data]; // copying the old datas array
// a deep copy is not needed as we are overriding the whole object below, and not setting a property of it. this does not mutate the state.
newArr[index] = e.target.value; // replace e.target.value with whatever you want to change it to
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
The accepted answer leads the developer into significant risk that they will mutate the source sequence, as witnessed in comments:
let newArr = [...data];
// oops! newArr[index] is in both newArr and data
// this might cause nasty bugs in React.
newArr[index][propertyName] = e.target.value;
This will mean that, in some cases, React does not pick up and render the changes.
The idiomatic way of doing this is by mapping your old array into a new one, swapping what you want to change for an updated item along the way.
setData(
data.map(item =>
item.id === index
? {...item, someProp : "changed", someOtherProp: 42}
: item
))
setDatas(datas=>({
...datas,
[index]: e.target.value
}))
with index being the target position and e.target.value the new value
You don't even need to be using the index ( except for the key if you want ) nor copying the old datas array,and can even do it inline or just pass data as an argument if you prefer updateFieldChanged to not be inline. It's done very quickly that way :
const initial_data = [
{
id: 1,
name: "john",
gender: "m",
},
{
id: 2,
name: "mary",
gender: "f",
},
];
const [datas, setDatas] = useState(initial_data);
return (
<div>
{datas.map((data, index) => (
<li key={index}>
<input
type="text"
value={data.name}
onChange={(e) => {
data.name = e.target.value;
setDatas([...datas]);
}}
/>
</li>
))}
</div>
);
};
This is what I do:
const [datas, setDatas] = useState([
{
id: 1,
name: "john",
gender: "m",
},
{
id: 2,
name: "mary",
gender: "f",
},
]);
const updateFieldChanged = (name, index) => (event) => {
let newArr = datas.map((item, i) => {
if (index == i) {
return { ...item, [name]: event.target.value };
} else {
return item;
}
});
setDatas(newArr);
};
return (
<React.Fragment>
{datas.map((data, index) => {
<li key={data.name}>
<input
type="text"
name="name"
value={data.name}
onChange={updateFieldChanged("name", index)}
/>
</li>;
<li key={data.gender}>
<input
type="text"
name="gender"
value={data.gender}
onChange={updateFieldChanged("gender", index)}
/>
</li>;
})}
</React.Fragment>
);
Spread the array before that. As you cannot update the hook directly without using the method returned by useState
const newState = [...originalState]
newState[index] = newValue
setOriginalState(newState)
This will modify the value of the state and update the useState hook if its an array of string.
const updateFieldChanged = index => e => {
name=e.target.name //key
let newArr = [...data]; // copying the old datas array
newArr[index][name] = e.target.value; //key and value
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
const [datas, setDatas] = useState([ { id: 1, name: 'john', gender: 'm' } { id: 2, name: 'mary', gender: 'f' } ]);
const updateFieldChanged = (index, e) => { const updateData = { ...data[index], name: e.target.name }
setData([...data.slice(0, index), updateData, ...data.slice(index + 1)]) }
I am late to reply but I had also same problem, so I got solution through this query. Have a look on it if it can help you.The example that I did is that I have a state , named OrderList, and so a setter for it is SetOrderList. To update a specific record in a list, am using SetOrderList and passing it a map function with that list of which I need to change, so I will compare Index or Id of my list, where it will match, I will change that specific record.
const Action = (Id, Status) => { //`enter code here`Call this function onChange or onClick event
setorderList([...orderList.map((order) =>
order.id === Id ? { ...order, status: Status } : order
),
]);
}
complete example for update value based on index and generate input based on for loop....
import React, { useState,useEffect } from "react";
export default function App() {
const [datas, setDatas] =useState([])
useEffect(() => {
console.log("datas");
console.log(datas);
}, [datas]);
const onchangeInput = (val, index) =>{
setDatas(datas=>({
...datas,
[index]: val.target.value
}))
console.log(datas);
}
return (
<>
{
(() => {
const inputs = [];
for (let i = 0; i < 20; i++){
console.log(i);
inputs.push(<input key={i} onChange={(val)=>{onchangeInput(val,i)}} />);
}
return inputs;
})()
}
</>
);
}
const MyCount = () =>{
const myData = [
{
id: 1,
name: 'john',
gender: 'm'
},
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [count, setCount] = useState(0);
const [datas, setDatas] = useState(myData);
const clkBtn = () =>{
setCount((c) => c + 1);
}
return(
<div>
<button onClick={clkBtn}>+ {count}</button>
{datas.map((data, index) => (
<li key={index}>
<input
type="text"
value={data.name}
onChange={(e) => {
data.name = e.target.value;
setDatas([...datas]);
}}
/>
</li>
))}
</div>
)
}
Base on #Steffan, thus use as your way:
const [arr,arrSet] = useState(array_value);
...
let newArr = [...arr];
arr.map((data,index) => {
newArr[index].somename= new_value;
});
arrSet(newArr);
Use useEffect to check new arr value.
A little late to the party, but it is an option to spread the contents of the array in a new object, then replacing the desired object in the selected index and finally producing an array from that result, it is a short answer, probably not the best for large arrays.
// data = [{name: 'tom', age: 15, etc...}, {name: 'jerry', age: 10, etc...}]
// index = 1
setData(Object.values({...data, [index]: {...data[index], name: 'the mouse' }}))
// data = [{name: 'tom', age: 15, etc...}, {name: 'the mouse', age: 10, etc...}]

When I remove an item from a array, how to change the status to deleted?

When the user adds a value to an object that is inside an array the status is updated to added.
I'm trying to do the same thing when that value is deleted, to update the status to deleted.
const initialName = [
{
name: "",
status: "",
},
];
export default function changeNames(){
const [name, setName] = useState([initialName ]);
const handleAdd = () => {
setName([...name, ""]);
};
const handleItemChanged = (event, index) => {
const value = event.target.value;
const list = [...name];
list[index] = { name: value + "-" + id, status: "added" };
setName(list);
};
...
}
So when I add an input field and type the name, the array looks like this:
[{…}]
0:
name: "John-id"
status: "added"
When I remove John from the array, I want smth like this:
[{…}]
0:
name: "John-id"
status: "deleted"
This is the remove function
const handleRemoveClick = (index) => {
const list = [...name];
list.splice(index, 1);
setName(list);
};
<div>
{name.map((o, i) => {
return (
<tr key={"item-" + i}>
<td>
<div>
<input
type="text"
value={o.item}
onChange={(event) => handleItemChanged(event, i)}
placeholder="Name it"
/>
</div>
</td>
<td>
<button type="button" onClick={handleRemoveClick}>
Delete
</button>
</td>
</tr>
);
})}
<div>
<button Click={handleAddClick}>
Add Language
</button>
</div>
</div>
);
How can I make it work?
I think this might help you.thank you
import { useState } from "react";
import "./styles.css";
export default function App() {
const [Name, setName] = useState([]);
const [input, setInput] = useState({ name: "", status: "" });
const handleItemChanged = (event, index) => {
const { value } = event.target;
setInput({ name: value + "-id", status: "added" });
};
const addHandler = () => {
setName((prev) => {
return [...prev, input];
});
};
const removeHandler = (i) => {
const arr = [...Name];
arr[i].status = "deleted";
setName(arr);
};
return (
<div className="App">
<input type="text" name="name" onChange={(e) =>
handleItemChanged(e)} />
<button onClick={addHandler} style={{ margin: "1rem" }}>
Add
</button>
<div>
{Name &&
Name.map((data, i) => {
return (
<div key={i}>
<h3>
{data.name} {data.status}
<button
style={{ margin: "1rem" }}
onClick={() => removeHandler(i)}
>
Delete
</button>
</h3>
</div>
);
})}
</div>
</div>
);
}
You need to change the "status" property, because there is no way of removing item and setting its property.
const handleRemoveClick = (event, index) => {
const list = [...name];
list[index].status = "deleted";
setName(list);
};
And later while rendering you have to either perform a check in map function (not really elegant), or first filter your array and then map it:
// first, not elegant in my opinion
...
{name.map(item => item.status !== "deleted" ? <div>item.name</div> : null)}
// second approach
...
{name.filter(item => item.status !== "deleted").map(item => <div>item.name</div>)}

How can I filter 2 arrays with 2 input field react?

I want to filter the data based on name and tag using 2 input fields. with the name it is filtering fine but how can I filter by tags.tagList and I am trying hard but didn't get any solution. I want to show data if someone search by name then they can show the data. Also in they can search data by tag but tag list in another array. how can solve this?
const Home = () => {
const [students, setStudents] = useState([]);
const [tags, setTags] = useState([{ idNumber: "", tagList: "" }]);
const [searchByName, setSearchByName] = useState("");
const [searchByTag, setSearchByTag] = useState("");
useEffect(() => {
const url = ".../students";
fetch(url)
.then((data) => data.json())
.then((data) => setStudents(data.students));
}, []);
const addTags = (id, tag, keyCode) => {
if (keyCode == 13) {
const newTag = [...tags, { idNumber: id, tagList: tag }];
setTags(newTag);
`document.getElementById(`tags${id}`).value = "";`
}
};
return (
<main className="student-container">
<div className="search-box">
<input
type="text"
placeholder="Search by name..."
onChange={(e) => setSearchByName(e.target.value)}
/>
</div>
<div className="search-box">
<input
type="text"
placeholder="Search by tag..."
onChange={(e) => setSearchByTag(e.target.value)}
/>
</div>
{students.map((student) => {
if (searchByName.length !== 0) {
if (
`${student.firstName} ${student.lastName}`
.toLocaleLowerCase()
.startsWith(searchByName.toLocaleLowerCase())
) {
return (
<StudentCard
key={student.id}
student={student}
addTags={addTags}
tags={tags}
/>
);
} else {
return null;
}
}
return (
<StudentCard
key={student.id}
student={student}
addTags={addTags}
tags={tags}
/>
);
})}
</main>
);
};
export default Home;
You want a map like object for tags to easily look up tagsList of a specific user while filtering like below.
{
id1: {id: id1, tagsList: [...]},
id2: {id: id2, tagsList: [...]},
id3: {id: id3, tagsList: [...]},
}
Here is a sample solution that can help. tagsMap is the above mentioned lookup object.
const students = [
{ id: 1, firstName: "AB", lastName: "CD" },
{ id: 2, firstName: "CD", lastName: "EF" },
{ id: 3, firstName: "EF", lastName: "GH" },
];
const tagsList = [
{ idNumber: 1, tagsList: ["TA", "TB", "TC"] },
{ idNumber: 2, tagsList: ["TC", "TD"] },
{ idNumber: 3, tagsList: ["TD", "TE"] },
];
const searchByName = "C";
const searchByTag = "TD";
const tagsMap = tagsList.reduce((acc, tag) => {
acc[tag.idNumber] = tag;
return acc;
}, {});
const studentNameMatches = (student, searchTerm) =>
`${student.firstName} ${student.lastName}`
.toLocaleLowerCase()
.startsWith(searchTerm.toLocaleLowerCase());
const studentHasTag = (student, tagName) =>
tagsMap[student.id]?.tagsList.includes(tagName);
const filteredStudents = students.filter(
(student) =>
studentNameMatches(student, searchByName) &&
studentHasTag(student, searchByTag)
);
// Students with name starting with "C" And has tag "TD"
console.log(filteredStudents);

Is there a way to onChange a nested array in useState?

I have a very simple state that is an array of objects. The second object has an array of objects inside that. I am able to change the basic state using an onChange handler that works well. However when trying to access the nested data, and change it I have problems.
The display first of all is giving me object obJect. I usually would map this to get it working but the value is going inside a value property.
import React, { useState } from "react";
import "./style.css";
const App = () => {
const [data, setData] = useState([
{ firstName: "Elon", lastName: "Musk" },
{
firstName: "Jeff",
lastName: "Bezos",
additionDetails: [{ worth: "Lots" }, { company: "Amazon" }],
},
]);
const handleChange = (e, i) => {
const clonedData = [...data];
clonedData[i][e.target.name] = e.target.value;
setData(clonedData);
};
console.log(Object.values(data[1].additionDetails[1]))
return (
<>
{data.map((x, i) => {
return [
<input
type="text"
name="firstName"
value={x.firstName}
onChange={(e) => handleChange(e, i)}
/>,
<input
type="text"
name="Worth"
value={x.additionDetails}
onChange={(e) => handleChange(e, i)}
/>,
<br></br>,
];
})}
</>
);
};
export default App;
Try this snippet. Since the additionDetails is an array of items, use the map and have the input field for each item.
Update the handleChange accordingly, based on the nested level. (when arg j exist means, it is additionalDetails)
const App = () => {
const [data, setData] = React.useState([
{ firstName: "Elon", lastName: "Musk" },
{
firstName: "Jeff",
lastName: "Bezos",
additionDetails: [{ worth: "Lots" }, { company: "Amazon" }],
},
]);
const handleChange = (e, i, j) => {
const clonedData = [...data];
if (j !== undefined) {
clonedData[i]["additionDetails"][j][e.target.name] = e.target.value;
} else {
clonedData[i][e.target.name] = e.target.value;
}
setData(clonedData);
};
console.log(Object.values(data[1].additionDetails[1]));
return (
<div>
{data.map((x, i) => {
return [
<input
type="text"
name="firstName"
value={x.firstName}
onChange={(e) => handleChange(e, i)}
/>,
x.additionDetails &&
x.additionDetails.map((obj, j) => (
<input
type="text"
name={Object.keys(obj)[0]}
value={Object.values(obj)[0]}
onChange={(e) => handleChange(e, i, j)}
/>
)),
<br></br>,
];
})}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="app"> Hello </div>

How do I update states `onChange` in an array of object in React Hooks

I have retrieved data stored using useState in an array of object, the data was then outputted into form fields. And now I want to be able to update the fields (state) as I type.
I have seen examples on people updating the state for property in array, but never for state in an array of object, so I don't know how to do it. I've got the index of the object passed to the callback function but I didn't know how to update the state using it.
// sample data structure
const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [data, setData] = useState([]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
setData() // ??
}
return (
<React.Fragment>
{data.map((data, index) => {
<li key={data.name}>
<input type="text" name="name" value={data.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
Here is how you do it:
// sample data structure
/* const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
] */ // make sure to set the default value in the useState call (I already fixed it)
const [data, setData] = useState([
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
let newArr = [...data]; // copying the old datas array
// a deep copy is not needed as we are overriding the whole object below, and not setting a property of it. this does not mutate the state.
newArr[index] = e.target.value; // replace e.target.value with whatever you want to change it to
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
The accepted answer leads the developer into significant risk that they will mutate the source sequence, as witnessed in comments:
let newArr = [...data];
// oops! newArr[index] is in both newArr and data
// this might cause nasty bugs in React.
newArr[index][propertyName] = e.target.value;
This will mean that, in some cases, React does not pick up and render the changes.
The idiomatic way of doing this is by mapping your old array into a new one, swapping what you want to change for an updated item along the way.
setData(
data.map(item =>
item.id === index
? {...item, someProp : "changed", someOtherProp: 42}
: item
))
setDatas(datas=>({
...datas,
[index]: e.target.value
}))
with index being the target position and e.target.value the new value
You don't even need to be using the index ( except for the key if you want ) nor copying the old datas array,and can even do it inline or just pass data as an argument if you prefer updateFieldChanged to not be inline. It's done very quickly that way :
const initial_data = [
{
id: 1,
name: "john",
gender: "m",
},
{
id: 2,
name: "mary",
gender: "f",
},
];
const [datas, setDatas] = useState(initial_data);
return (
<div>
{datas.map((data, index) => (
<li key={index}>
<input
type="text"
value={data.name}
onChange={(e) => {
data.name = e.target.value;
setDatas([...datas]);
}}
/>
</li>
))}
</div>
);
};
This is what I do:
const [datas, setDatas] = useState([
{
id: 1,
name: "john",
gender: "m",
},
{
id: 2,
name: "mary",
gender: "f",
},
]);
const updateFieldChanged = (name, index) => (event) => {
let newArr = datas.map((item, i) => {
if (index == i) {
return { ...item, [name]: event.target.value };
} else {
return item;
}
});
setDatas(newArr);
};
return (
<React.Fragment>
{datas.map((data, index) => {
<li key={data.name}>
<input
type="text"
name="name"
value={data.name}
onChange={updateFieldChanged("name", index)}
/>
</li>;
<li key={data.gender}>
<input
type="text"
name="gender"
value={data.gender}
onChange={updateFieldChanged("gender", index)}
/>
</li>;
})}
</React.Fragment>
);
Spread the array before that. As you cannot update the hook directly without using the method returned by useState
const newState = [...originalState]
newState[index] = newValue
setOriginalState(newState)
This will modify the value of the state and update the useState hook if its an array of string.
const updateFieldChanged = index => e => {
name=e.target.name //key
let newArr = [...data]; // copying the old datas array
newArr[index][name] = e.target.value; //key and value
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
const [datas, setDatas] = useState([ { id: 1, name: 'john', gender: 'm' } { id: 2, name: 'mary', gender: 'f' } ]);
const updateFieldChanged = (index, e) => { const updateData = { ...data[index], name: e.target.name }
setData([...data.slice(0, index), updateData, ...data.slice(index + 1)]) }
I am late to reply but I had also same problem, so I got solution through this query. Have a look on it if it can help you.The example that I did is that I have a state , named OrderList, and so a setter for it is SetOrderList. To update a specific record in a list, am using SetOrderList and passing it a map function with that list of which I need to change, so I will compare Index or Id of my list, where it will match, I will change that specific record.
const Action = (Id, Status) => { //`enter code here`Call this function onChange or onClick event
setorderList([...orderList.map((order) =>
order.id === Id ? { ...order, status: Status } : order
),
]);
}
complete example for update value based on index and generate input based on for loop....
import React, { useState,useEffect } from "react";
export default function App() {
const [datas, setDatas] =useState([])
useEffect(() => {
console.log("datas");
console.log(datas);
}, [datas]);
const onchangeInput = (val, index) =>{
setDatas(datas=>({
...datas,
[index]: val.target.value
}))
console.log(datas);
}
return (
<>
{
(() => {
const inputs = [];
for (let i = 0; i < 20; i++){
console.log(i);
inputs.push(<input key={i} onChange={(val)=>{onchangeInput(val,i)}} />);
}
return inputs;
})()
}
</>
);
}
const MyCount = () =>{
const myData = [
{
id: 1,
name: 'john',
gender: 'm'
},
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [count, setCount] = useState(0);
const [datas, setDatas] = useState(myData);
const clkBtn = () =>{
setCount((c) => c + 1);
}
return(
<div>
<button onClick={clkBtn}>+ {count}</button>
{datas.map((data, index) => (
<li key={index}>
<input
type="text"
value={data.name}
onChange={(e) => {
data.name = e.target.value;
setDatas([...datas]);
}}
/>
</li>
))}
</div>
)
}
Base on #Steffan, thus use as your way:
const [arr,arrSet] = useState(array_value);
...
let newArr = [...arr];
arr.map((data,index) => {
newArr[index].somename= new_value;
});
arrSet(newArr);
Use useEffect to check new arr value.
A little late to the party, but it is an option to spread the contents of the array in a new object, then replacing the desired object in the selected index and finally producing an array from that result, it is a short answer, probably not the best for large arrays.
// data = [{name: 'tom', age: 15, etc...}, {name: 'jerry', age: 10, etc...}]
// index = 1
setData(Object.values({...data, [index]: {...data[index], name: 'the mouse' }}))
// data = [{name: 'tom', age: 15, etc...}, {name: 'the mouse', age: 10, etc...}]

Categories