const listRef = useRef(null);
      const [text, setText] = useState('');
      const [todos, setTodos] = useState(
        initialTodos
      );
    
      function handleAdd() {
        const newTodo = { id: nextId++, text: text };
        flushSync(() => {
          setText('');
          setTodos([ ...todos, newTodo]);      
        });
        listRef.current.lastChild.scrollIntoView({
          behavior: 'smooth',
          block: 'nearest'
        });
      }
    
      return (
        <>
          <button onClick={handleAdd}>
            Add
          </button>
          <input
            value={text}
            onChange={e => setText(e.target.value)}
          />
          <ul ref={listRef}>
            {todos.map(todo => (
              <li key={todo.id}>{todo.text}</li>
            ))}
          </ul>
        </>
      );
    }
    
    let nextId = 0;
    let initialTodos = [];
    for (let i = 0; i < 20; i++) {
      initialTodos.push({
        id: nextId++,
        text: 'Todo #' + (i + 1)
      });
    }
    
    

Show more

##  Best practices for DOM manipulation with refs

Refs are an escape hatch. You should only use them when you have to “step
outside React”. Common examples of this include managing focus, scroll
position, or calling browser APIs that React does not expose.

If you stick to non-destructive actions like focusing and scrolling, you
shouldn’t encounter any problems. However, if you try to **modify** the DOM
manually, you can risk conflicting with the changes React is making.

To illustrate this problem, this example includes a welcome message and two
buttons. The first button toggles its presence using [ conditional rendering
](/learn/conditional-rendering) and [ state ](/learn/state-a-components-
memory) , as you would usually do in React. The second button uses the [ `
remove() ` DOM API ](https://developer.mozilla.org/en-
US/docs/Web/API/Element/remove) to forcefully remove it from the DOM outside
of React’s control.

Try pressing “Toggle with setState” a few times. The message should disappear
and appear again. Then press “Remove from the DOM”. This will forcefully
remove it. Finally, press “Toggle with setState”:

App.js

App.js

Reset  [ Fork
](https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-
react-app "Open in CodeSandbox")

    
    
    import { useState, useRef } from 'react';
    
    export default function Counter() {
      const [show, setShow] = useState(true);
      const ref = useRef(null);
    
      return (
        <div>
          <button
            onClick={() => {
              setShow(!show);
            }}>
            Toggle with setState
          </button>
          <button
            onClick={() => {
              ref.current.remove();
            }}>
            Remove from the DOM
          </button>
          {show && <p ref={ref}>Hello world</p>}
        </div>
      );
    }
    
    

Show more

After you’ve manually removed the DOM element, trying to use ` setState ` to
show it again will lead to a crash. This is because you’ve changed the DOM,
and React doesn’t know how to continue managing it correctly.

**Avoid changing DOM nodes managed by React.** Modifying, adding children to,
or removing children from elements that are managed by React can lead to
inconsistent visual results or crashes like above.

However, this doesn’t mean that you can’t do it at all. It requires caution.
**You can safely modify parts of the DOM that React has _no reason_ to update.
** For example, if some ` <div> ` is always empty in the JSX, React won’t have
a reason to touch its children list. Therefore, it is safe to manually add or
remove elements there.

##  Recap

  * Refs are a generic concept, but most often you’ll use them to hold DOM elements. 
  * You instruct React to put a DOM node into ` myRef.current ` by passing ` <div ref={myRef}> ` . 
  * Usually, you will use refs for non-destructive actions like focusing, scrolling, or measuring