My colleague has been working on a small project and needs my help with it, I have barely any experience in react and have been trying to use my knowledge from other languages to handle this problem, however I have come to my limits after searching the web for hours and not finding an efficient way to handle this issue without having to rewrite most of the code.
So the problem is the following: We have customer orders with different variables, (i.e. Loading Country) These are displayed in a table like this (we map over the data and print this tr/td for every order):
<td><input type="text" onChange={(e) => setLoadingCountry(e.target.value)} className={"lcountry" data.rowid} value={data.array_options.options_lcountry}></input></td>
<td><input type="text" onChange={(e) => setLoadingZip(e.target.value)} className={"lpostcode" data.rowid} value={data.array_options.options_lpostcode}></input></td>
setLoadingCountry
is used for a useState.
With an update button for each row at the end:
<td><button className={data.id} onClick={(e) => handleCoData(e.target.className)}>Update</button></td>
handleCoData
is a function I made, since we get the initial data from a json and I put the json into a useState array, this function handles that array after button click like the following:
const handleCoData = (coID) => {
setCoData(
coData.map((co) =>
co.id == coID
? {...co, array_options: {
options_lcountry: loadingCountry,
options_lpostcode: loadingZip
}}
: {...co}
)
);
console.log(coID);
console.log(coData);
};
Now: One Problem, which is the smaller one, is that we can only update one row at a time, since changing 2 countries from 2 rows without updating inbetween will overwrite the value of the first loading country in the useState. The bigger problem is that, when you only want to change the loading country, and then click update, the Zip is either empty, because it was never set by the use state, or it has the value from another row which was changed previously. Is there any way to get the value from that specific row for that specific field?
If this question needs changes or additional information please tell me so, as I said I do not have much experience in React, and it is not my project either so I have no idea what might be needed to fully understand the issue.
Thank you.
Edit: I was not able to make a codepen, since it would show a syntax error that I can't figure out quickly ( there isn't one in the actual code), but here is the code which includes all the things needed.
//import Data from "../../data.json";
const CombinedComponents = (apiKey) => {
const [loadingCountry, setLoadingCountry] = useState([]);
const [loadingZip, setLoadingZip] = useState([]);
const [coData, setCoData] = useState(Data);
console.log(coData);
const handleCoData = (coID) => {
setCoData(
coData.map((co) =>
co.id == coID
? {
...co,
array_options: {
options_lcountry: loadingCountry,
options_lpostcode: loadingZip
}
}
: { ...co }
)
);
console.log(coID);
console.log(coData);
};
return (
<div>
<table>
{visible && <TableHeadView1 />}
<tbody>
{coData.map((data) => (
<tr>
<td>{data.ref}</td>
<td>
<input
type="text"
onChange={(e) => setLoadingCountry(e.target.value)}
className={"lcountry" data.rowid}
value={data.array_options.options_lcountry}
></input>
</td>
<td>
<input
type="text"
onChange={(e) => setLoadingZip(e.target.value)}
className={"lpostcode" data.rowid}
value={data.array_options.options_lpostcode}
></input>
</td>
<td>
<button
className={data.id}
onClick={(e) => handleCoData(e.target.className)}
>
Update
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
This is what the array coData
looks like (ofcourse with many more entries):
[{
"id": "11480",
"array_options": {
"options_lcountry": "1",
"options_lpostcode": "80190",
}
}]
CodePudding user response:
Here your problem resides in using a single state value for handling multiple input values. If I was to do it, I'd only use the main array all the time, giving me multiple fields to change from. Currently, your update button only handles the React state, which is purely front end. You could use it to send the update to your backend instead.
My take on this one would be using these functions for inputs 'onChange' props:
const handleCountryUpdate = (id, value) => {
coData.find(it => it.id === id).array_options.options_lcountry = value;
setCoData([...coData]); // Using an array copy to override the state
};
const handlePostCodeUpdate = (id, value) => {
coData.find(it => it.id === id).array_options.options_lpostcode = value;
setCoData([...coData]);
};
...
<input type="text"
onChange={(e) => handlePostCodeUpdate(data.id, e.target.value)}
className={"lpostcode" data.rowid}
value={data.array_options.options_lpostcode}>
</input>
Another way around would be to use a child component state to do the job
...
{coData.map((data) => <TableRow key={data.id}
data={data}
handleCoData={handleCoData}/>)}
...
const TableRow = props => {
const {data, handleCoData} = props;
const [loadingCountry, setLoadingCountry] = useState(data.array_options.options_lcountry);
const [loadingZip, setLoadingZip] = useState(data.array_options.options_lpostcode);
return (
<tr>
<td>{data.ref}</td>
<td>
<input
type="text"
onChange={(e) => setLoadingCountry(e.target.value)}
className={"lcountry" data.rowid}
value={data.array_options.options_lcountry}
></input>
</td>
<td>
<input
type="text"
onChange={(e) => setLoadingZip(e.target.value)}
className={"lpostcode" data.rowid}
value={data.array_options.options_lpostcode}
></input>
</td>
<td>
<button
className={data.id}
onClick={(e) => handleCoData(e.target.className)}
>
Update
</button>
</td>
</tr>
);
}