Let suppose I have the component like this:
export function ColorComponent ({color}: {color: TColor}) {
let textInputRef = useRef(null);
return (
<input
key={color.id}
ref={textInputRef}
type="text" value={color.text}
onKeyPress = {e => store.dispatch(colorTextKeyPressAction({colorId: color.id, e}))}
/>
);
}
And the reducer case is:
.addCase(a.colorTextKeyPress, (state, action) => {
let {colorId, e} = action.payload;
if (e.key == 'Enter') {
e.currentTarget.blur();
doUsefulStuff(colorId);
}
})
If I leave the code like that, I get an error from react-toolkit: "A non-serializable value was detected in an action".
No idea why tookit doesn't like a non-serializable value in an action — it doesn't go into the state anyway.
I can rewrite the component event handler like:
onKeyPress = {e => store.dispatch(colorTextKeyPressAction({colorId: color.id, keyCode: e.key}))}
to pass only a string in the action. Only to be unable to call e.currentTarget.blur()
.
As I understand I can use "refs" like textInputRef
above to directly access input field — but how I pass that ref to the reducer anyway, wthout again getting an error about "non-serializable value"?
I'd happily use some bool flag to control focused/blurred state of an input if React had one, but AFAIK it hasn't.
The possible answer to my own question.
I have recalled that a reducer purpose is only to alter the state, without any other side effects like blurring. And that a reducer isn't the same as a callback. So I only have to write the handler and call blur() and the reducer inside.
export function ColorComponent ({color}: {color: TColor}) {
let keyPressHandler = ({colorId, e}: {colorId: number, e: React.KeyboardEvent<HTMLInputElement>}) => {
if (e.key == 'Enter') {
e.currentTarget.blur();
store.dispatch(colorTextKeyPressAction({colorId: color.id}));
}
};
return (
<input
key={color.id}
type="text" value={color.text}
onKeyPress = {e => keyPressHandler({colorId: color.id, e})}
/>
);
}
The reducer then turns into simple
.addCase(a.colorTextKeyPress, (state, action) => {
doUsefulStuff(state, action.payload.colorId);
})