React: set focus on componentDidMount, how to do it with hooks?
Asked Answered
W

2

18

In React, with classes I can set the focus to an input when the component loads, something like this:

class Foo extends React.Component {
    txt1 = null;

    componentDidMount() {
        this.txt1.focus();
    }

    render() {
        return (
            <input type="text"
                ref={e => this.txt1 = e}/>
        );
    }
}

I'm trying to rewrite this component using the new hooks proposal.

I suppose I should use useEffect instead of componentDidMount, but how can I rewrite the focus logic?

Whaleboat answered 7/11, 2018 at 11:22 Comment(1)
Just wanted to mention, that it is also possible to set the autofocus property on the input itself without the need of JS like: <input type="text" autofocus="true" />. I know this is not a direct answer to your question, that's why I put it in a comment and I don't want to invalidate the given answer, I would do it the same way. I just think in many cases it's enough to just set the property directly and be done with it.Resonant
M
34

You can use the useRef hook to create a ref, and then focus it in a useEffect hook with an empty array as second argument to make sure it is only run after the initial render.

const { useRef, useEffect } = React;

function Foo() {
  const txt1 = useRef(null);

  useEffect(() => {
    txt1.current.focus();
  }, []);

  return <input type="text" ref={txt1} />;
}

ReactDOM.render(<Foo />, document.getElementById("root"));
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>

<div id="root"></div>
Moke answered 7/11, 2018 at 11:27 Comment(4)
Why isn't txt1 null when it's first called?Neill
@Neill The argument given to useRef is what value the ref's current property should be, so the initial value is { current: null }. Giving an empty array as second argument to useEffect makes it so the effect is only run once after the initial render, which is what OP wanted.Moke
Ah, thanks, I forgot that useEffect runs after the initial render not beforeNeill
This will throw a warning when using eslint "React Hook useEffect has a missing dependency: 'text1'. Either include it or remove the dependency array.eslintreact-hooks/exhaustive-deps"Singlecross
P
0

According to official docs: https://reactjs.org/docs/hooks-reference.html#useref you can use useRef.

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

A common use case is to access a child imperatively:

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Essentially, useRef is like a “box” that can hold a mutable value in its .current property.

You might be familiar with refs primarily as a way to access the DOM. If you pass a ref object to React with <div ref={myRef} />, React will set its .current property to the corresponding DOM node whenever that node changes.

However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.

This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.

Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.

Penstemon answered 19/9, 2019 at 11:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.