Home > Blockchain >  React setState to toggle input's disabled and focus
React setState to toggle input's disabled and focus

Time:01-18

I'm using useState in the following way to toggle editing an input:

const [editing, setEditing] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);

// returning:

<input
  ref={inputRef}
  disabled={!editing}
  type="text"
  name="name"
  id="name"
  defaultValue={organization?.name}
/>
<button
  onClick={() => {
    setEditing(!editing);
    // inputRef.current?.focus();
  }}
  type="button"
>
  <span>{editing ? "save" : "edit"}</span>
</button>

When the user clicks on "edit", editing becomes true which toggles the <input> to be no longer disabled and the input should be focused but it is not.

In order to get this working, I ended up using useEffect the following way:

useEffect(() => {
  if (editing) {
    inputRef.current?.focus();
  }
}, [editing, inputRef]);

This works, but it just seems a bit much. Is there a way to "await" setState or something to ensure the new state took effect, so that I can then do this focus()?

CodePudding user response:

The correct way of doing this for functional components is as you've done it, with useEffect.

React does not support state updates through the useState Hook, as Component#setState does.

If you wish to use a class component, however, then you can add a callback to the second argument of setState:

import React, { Component } from "react";

export default class MyComponent extends Component {
    constructor(props) {
        super(props);

        this.inputRef = React.createRef();
    };

    render() {
        return (
            <>
                <input
                ref={this.inputRef}
                disabled={!this.state?.editing}
                type="text"
                name="name"
                id="name"
                />
                <button
                onClick={() => {
                    this.setState({ editing: !this.state?.editing }, () => this.inputRef.current?.focus());
                    // inputRef.current?.focus();
                }}
                type="button"
                >
                <span>{this.state?.editing ? "save" : "edit"}</span>
                </button>
            </>
        );
    };
};

Which executes after the state has been set.

By attempting to use a callback argument in your set (state) function, React would throw the following error:

Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().

  • Related