I have a React component where input values for multiple players should update independently. When I toggle a state, the first input’s value incorrectly propagates to all other inputs. Additionally, clearing the values leaves residual values in some inputs. I need help fixing this synchronization issue.
Here is my current code implementation but the problem is when i change the value of the first input if it has 4 letters lets say, i get 3 in all the others also when i delete all from the first one
in all the others one number or letter is always staying is not deleting all
const getFilteredData = (
data: IPlayerTestsResponse[],
categoryId: string
) => {
if (!categoryId) return null;
return data.find((data) => data.player_test_id === categoryId);
};
const getTestResults = (
data: IPlayerTestsResponse[],
categoryId: string,
mode: 'edit' | 'add'
) => {
const result: {
[userId: string]: { target: string; result: string };
} = {};
if (mode === 'edit' && categoryId) {
const filteredData = getFilteredData(data, categoryId);
if (!filteredData) return result;
filteredData.results.forEach(
(resultData) =>
(result[resultData.user_id] = {
target: resultData.target.toString(),
result: resultData.result.toString()
})
);
} else {
players.forEach((player) => {
result[player.user_id] = {
target: '',
result: ''
};
});
}
return result;
};
const [testResults, setTestResults] = useState<{
[userId: string]: {
target: string;
result: string;
};
}>(getTestResults(testResultsData, dropdownInputValue, mode));
useEffect(() => {
if (dropdownInputValue) {
const updatedTestResults = getTestResults(
testResultsData,
dropdownInputValue,
mode
);
setTestResults(updatedTestResults);
} else {
const resetResults = getTestResults(testResultsData, '', 'add');
setTestResults(resetResults);
}
}, [dropdownInputValue, mode, testResultsData]);
const handleTestResultChange = (
userId: string,
type: 'target' | 'result',
value: string
) => {
setTestResults((prevResults) => {
const updatedResults = { ...prevResults };
if (type === 'target' && target) {
const keys = Object.keys(prevResults);
const firstTargetValue = keys.length ? prevResults[keys[0]].target : '';
keys.forEach((key) => {
updatedResults[key] = {
...updatedResults[key],
target: key === userId ? value : firstTargetValue
};
});
} else {
updatedResults[userId] = {
...updatedResults[userId],
[type]: value
};
}
return updatedResults;
});
};
const handleTargetChange = () => {
setTarget((prevTarget) => {
const newTarget = !prevTarget;
setTestResults((prevResults) => {
const updatedResults = { ...prevResults };
if (newTarget) {
const keys = Object.keys(prevResults);
const firstTargetValue = keys.length
? prevResults[keys[0]].target
: '';
keys.forEach((userId) => {
updatedResults[userId] = {
...updatedResults[userId],
target: firstTargetValue
};
});
} else {
Object.keys(prevResults).forEach((userId) => {
updatedResults[userId] = {
...updatedResults[userId],
target: ''
};
});
}
return updatedResults;
});
return newTarget;
});
};
import TestResultTableRow from '../TestResultTableRow/TestResultTableRow';
import './TestResultTable.scss';
type Props = {
players: IPlayer[];
target: boolean;
handleTestResultChange: (
userId: string,
type: 'target' | 'result',
value: string
) => void;
testResults: {
[userId: string]: {
target: string;
result: string;
};
};
};
export default function TestResultTable({
target,
handleTestResultChange,
testResults,
players
}: Props) {
// console.log('Players', players);
// console.log('testResults BEFORE MAP', testResults);
let counter = 0;
return (
<div className="test-result-table">
<>
<TestResultTableRow gridColumns={3}>
<div className="test-result-table__name">PLAYER</div>
<div className="test-result-table__target">TARGET</div>
<div className="test-result-table__target">RESULT</div>
</TestResultTableRow>
</>
<div className="test-result-table__body">
{players.map((player, index) => {
counter++;
console.log(index, counter);
return (
<TestResultTableRow key={player.user_id} gridColumns={3}>
<div className="test-result-table__body__name">{`${player.firstname} ${player.lastname}`}</div>
<input
className={`test-result-table__body__target ${target && index !== 0 ? ' test-result-table__body__target--filled' : ''}`}
name="target[]"
type="text"
value={testResults[player.user_id]?.target || ''}
onChange={(e) =>
handleTestResultChange(
player.user_id,
'target',
e.target.value
)
}
disabled={target && index !== 0}
/>
<input
className="test-result-table__body__target "
type="text"
value={testResults[player.user_id]?.result || ''}
onChange={(e) =>
handleTestResultChange(
player.user_id,
'result',
e.target.value
)
}
/>
</TestResultTableRow>
);
})}
</div>
</div>
);
}
2
Answers
I think update by index will be best option to update the state, one sample added below, please check, this may help you.
Please check handleChange function
When input values are cleared, some inputs retain residual values—a single letter or number—instead of erasing all of the data. Each input for different players should update independently.
Here you may update code as following:
Next, Make sure that the state from testResults is appropriately reflected in each input field’s value prop.