Reputation: 275
My component like this:
import React, { useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import './SizeAndColor.css';
const initialData = {
sizes: ['S', 'M', 'L', 'XL'],
data: [
{
rowId: uuidv4(),
color: 'White',
sizes: [
{ fieldId: uuidv4(), sizeName: 'S', qty: 10 },
{ fieldId: uuidv4(), sizeName: 'M', qty: 20 },
{ fieldId: uuidv4(), sizeName: 'L', qty: 30 },
{ fieldId: uuidv4(), sizeName: 'XL', qty: 40 }
]
},
{
rowId: uuidv4(),
color: 'Gray',
sizes: [
{ fieldId: uuidv4(), sizeName: 'S', qty: 50 },
{ fieldId: uuidv4(), sizeName: 'M', qty: 60 },
{ fieldId: uuidv4(), sizeName: 'L', qty: 70 },
{ fieldId: uuidv4(), sizeName: 'XL', qty: 80 }
]
}
]
};
const initialFieldValue = {
id: 0,
initialData: initialData
};
export default function SizeAndColor() {
const [state, setState] = useState(initialFieldValue);
const onChange = (e, rowId, fieldId) => {
const { name, value } = e.target;
const updatedData = state.initialData.data.map(row => {
if (rowId === row.rowId) {
row.sizes.map(item => {
if (fieldId === item.fieldId) {
if (!isNaN(value)) {
item[name] = value;
}
}
return item;
});
}
return row;
});
setState({ ...state, initialData: { ...initialData, data: updatedData } });
};
return (
<div>
<table>
<thead>
<tr>
<td></td>
{state.initialData.sizes.map(size => (
<td key={uuidv4()}>{size}</td>
))}
</tr>
</thead>
<tbody>
{state.initialData.data.map(item => (
<tr key={uuidv4()}>
<td>{item.color}</td>
{item.sizes.map(size => (
<td key={uuidv4()}>
<input
type="text"
name="qty"
value={size.qty}
onChange={e => {
onChange(e, item.rowId, size.fieldId);
}}
/>
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
When I start typing on any input field, It work only first key press. Than again have to click the input field. Than again after first key press, can not type anything.
I tried it with different scenario. If my initialData
object doesn't have nested property, It works fine.
how do i solve this issue, please help me.
Upvotes: 0
Views: 609
Reputation: 202605
You are generating new React keys each render.
<div>
<table>
<thead>
<tr>
<td></td>
{state.initialData.sizes.map(size => (
<td key={uuidv4()}>{size}</td> // <-- here
))}
</tr>
</thead>
<tbody>
{state.initialData.data.map(item => (
<tr key={uuidv4()}> // <-- here
<td>{item.color}</td>
{item.sizes.map(size => (
<td key={uuidv4()}> // <-- here
<input
type="text"
name="qty"
value={size.qty}
onChange={e => {
onChange(e, item.rowId, size.fieldId);
}}
/>
</td>
))}
</tr>
))}
</tbody>
</table>
When you generate a new React key each render cycle it tells React that these are new components, so the previous ones are unmounted and a new component is instantiated. This is why the inputs lose focus.
Use the ids generated in your data so the React keys are stable, i.e. they don't change from render to render.
<div>
<table>
<thead>
<tr>
<td></td>
{state.initialData.sizes.map((size, index) => (
// use index since size array likely doesn't change
<td key={index}>{size}</td>
))}
</tr>
</thead>
<tbody>
{state.initialData.data.map(item => (
<tr key={item.rowId}> // <-- use row id
<td>{item.color}</td>
{item.sizes.map(size => (
<td key={size.fieldId}> // <-- use field id
<input
type="text"
name="qty"
value={size.qty}
onChange={e => {
onChange(e, item.rowId, size.fieldId);
}}
/>
</td>
))}
</tr>
))}
</tbody>
</table>
Upvotes: 1
Reputation: 5036
The reason for that is that you generate random keys for input on each render, so React thinks it's a new unrelated field that doesn't need to be focused, you need to replace key={uuidv4()}
with key={item.rowId}
and key={size.fieldId}
Upvotes: 1