I found this clever example of using a single change function to handle multiple text inputs and state changes for React and Typescript. The example, naturally, uses a normal string type:
type MyEntity = {
name: string;
// would work with any other fields
}
const handleChange = (fieldName: keyof MyEntity) => (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setValues({ ...values, [fieldName]: e.currentTarget.value });
However, my type for strings is a dictionary. Eg:
{[index: string]: string}
...where the index is a language code string and its corresponding value is a string for that language's content. For example, a name object would look like, name:{'en': 'Michael', 'es': 'Miguel'}
Here's my current field-specific change handler that I'd like to make work for all text fields. Note- selectedLanguage is a string state variable with the language code the user selected, omitted for brevity. See codesandbox link below for full version):
type StringLanguage = {
[index: string] : string;
}
interface MyEntity {
name: StringLanguage;
}
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setData((prevData) => {
return {
...prevData,
name: {
...prevData.name,
[selectedLanguage]: e.target.value
}
};
});
};
Here's my attempt to use one like the example I found.
const handleChange2 = (fieldName: keyof MyEntity) => (
e: React.ChangeEvent<HTMLInputElement>
) => {
setData((prevData) => {
return {
...prevData,
[fieldName]: {
...prevData[fieldName],
[selectedLanguage]: e.currentTarget.value
}
};
});
};
TS is giving me the error, "Spread types may only be created from object types. ts(2968)" on the line of code:
...prevData[fieldName]
But prevData is an object and in the field-specific version it works. How do I get this more generic function to work with my dictionary type, StringLanguage?:
Here's the usage for a text input:
onChange={(e) => handleChange2("name")}
Finally, here is a codesandbox with the complete example of what I'm doing. Feel free to comment out code to experiment. I've left the version that works active and commented out what doesn't work from above:
Hey the thing is that in your interface
MyEntity
there is anid: string
property, that is why the ts complier complains.If you want to do something generic it would be something like this