Search code examples
reactjsredux-toolkitreact-typescript

How can I blur text input in React with react-toolkit?


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.


Solution

  • 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);
    })