I have a form in my React App. The form has some fields itself, but also allows the user to upload images and comment them. The form renders a list of uploaded images (previews) and an input field for comment for each of them (ImageList component, which renders multiple ImageItems).
I store data for the uploaded images in a Redux (with Toolkit) store (files).
files is an array of IFile:
interface IFile {
file: { name: string; url: string; size: number };
dto: { comment?: string };
}
Components look roughly like this:
// CreateForm.tsx
const { files } = useSelector((state: RootState) => state.createSpot);
return (
<form>
{/* other inputs */}
<ImageList files={files}/>
</form>
)
// ImageList.tsx
return (
<div>
{files.map((file, i) => (
<ImageItem
key={file.file.url}
file={file}
index={i}
/>
))}
</div>
)
ImageItem is just an <img/> and a text <input/>.
Submitting the form I also submit the images and corresponding comments. I want to somehow store those comments or, at least, receive them on submit and submit with the form.
I tried explicitly binding the inputs of each file to Redux store. I created a reducer to update a file's comment by its unique url:
// createSpot.reducer.ts
updateFileComment(
state,
action: PayloadAction<{ url: IFile["file"]["url"]; value: string }>
) {
const file = state.files.find((f) => f.file.url === action.payload.url);
if (file) file.dto.comment = action.payload.value;
},
My ImageItem looked like this:
// ImageItem.tsx
const ImageItem: React.FC<ImageItemProps> = ({ file }: { file: IFile }) => {
const dispatch = useAppDispatch();
return (
<>
<img src={file.file.url} alt={file.file.name} />
<textarea
placeholder="Comment"
value={file.dto.comment}
onChange={(e) => {
dispatch(
updateFileComment({
url: file.file.url,
value: e.target.value,
})
);
}}
/>
</>
);
};
While it seems to work as intended, it is obiously very expensive to dispatch such action on each character typed.
So is there some elegant and optimized way around this issue? I feel I'm missing something plain.
Thanks in advance.
textareaelement'sonChangehandler so you are not dispatching an action for each individual change.