Атрибут ref. Хук useRef и createRef

Последнее обновление: 22.03.2025

С помощью атрибута ref React позволяет напрямую ссылаться на любой элемент веб-страницы, который прикреплен к определенной ссылке. Для создания подобной ссылки для функциональных компонентов можно применять хук useRef(), а для компонентов-классов - специальную функцию createRef().

Хук useRef

Хук useRef позволяет сохранить некоторый объект, который можно можно изменять и который хранится в течение всей жизни компонента.

В качестве параметра функция useRef() принимает начальное значение хранимого объекта. А возвращаемое значение - ссылка-объект, из свойства current которого можно получить хранимое значение.

const refUser = React.useRef("Tom");
console.log(refUser.current);	// Tom

Расспространенным примером применения useRef является хранение ссылки на html-элементы внутри компонента:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel" data-type="module">
        import React from "https://esm.sh/react@19?dev";
        import ReactDOM from "https://esm.sh/react-dom@19/client?dev";
        

        function UserForm() {
            const nameField = React.useRef(null);
            const send = () => {
                // свойство current указывает на элемент input
                const inputElement = nameField.current;
                console.log("Имя: " + inputElement.value);
            };
            return (
                <div>
                    <input ref={nameField} />
                    <button onClick={send}>Отправить</button>
                </div>
            );
        }
        ReactDOM.createRoot(
            document.getElementById("app")
        )
        .render(
            <UserForm />
        );
    </script>
</body>
</html>

Здесь в компоненте сначала создается ссылка ref:

const nameField = React.useRef(null);

В данном случае нам начальное значение не важно, поэтому в useRef передается значение null. Однако в html-коде компонента определено текствое поле ввода:

<input ref={nameField} />

С помощью атрибута ref устанавливаем привязку этого поля к ссылке nameField. То есть через свойство nameField.current мы сможем получить объект, который представляет это поле ввода <input>.

const inputElement = nameField.current;
console.log("Имя: " + inputElement.value);

Получив объект, который представляет поле ввода, мы сможем выполнять с ним необходимые операции. В данном случае имитируется отправка введенного значения - ввод на консоль значения свойства value поля ввода:

хук useRef в React

Но это могут быть самые различные действия - получение и изменение свойств или вызов методов.

Однако только операциями с элементами html применение useRef не ограничивается. В реальности useRef может хранить любой объект, и это может быть полезно в различных ситуациях.

Например, рассмотрим ситуацию, когда вначале компонент загружает состояние из LocalStorage, а после окончания работы с компонентом при завершении его жизненного цикла он сохраняет состояние обратно в LocalStorage. На первый взгляд мы можем обойтись одним хуком useEffect:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel" data-type="module">
        import React from "https://esm.sh/react@19?dev";
        import ReactDOM from "https://esm.sh/react-dom@19/client?dev";
        

        const root = ReactDOM.createRoot(
            document.getElementById("app")
        );
        function UserForm() {
            const [name, setName] = React.useState("Tom");
        
            React.useEffect(() => {    
                // извлекаем данные из localStorage
                const userName = localStorage.getItem("userName");
                // если в localStorage есть такой объект
                if(userName!==null) {
                    setName(userName);
                    console.log("Got!");
                }
                
                // сохраняем данные в localStorage
                return()=>{
                    console.log(name);
                    localStorage.setItem("userName", name); 
                    console.log("Saved!");
                }
            },
            []); // эффект срабатывает только один раз - при самом первом рендеринге
            
            const changeName = (event) => setName(event.target.value);
            const unmount =() => root.unmount();
          
            return (
                <div>
                <h3>Имя: {name}</h3>
                    
                <div>
                    <p>Имя: <input value={name} onChange={changeName} /></p>
                    <button onClick={unmount}>Unmount</button>
                </div>
                </div>
            );
        }

        root.render(
            <UserForm />
        );
    </script>
</body>
</html>

Вначале в компоненте определяет начальное состояние в виде переменной name:

const [name, setName] = React.useState("Tom");

Далее в хуке React.useEffect() загружаем данные из LocalStorage:

const userName = localStorage.getItem("userName");

И чтобы сохранять данные, оператору return передается функция сохранения данных:

return ()=>{
	console.log(name);
	localStorage.setItem("userName", name); 
	console.log("Saved!");
}

Поскольку нам нужно, чтобы эффект срабатывал только один раз - извлечение данных происходило при загрузке компонента, а сохранение данных при удалении компонента в конце его работы, в хук useEffect передаются пустые скобки:

React.useEffect(() => {    
         // ....................
},
[]); 	// эффект вызывается только один раз

Для имитации удаления компонента и завершения его жизненного цикла в нем предумотрена кнопка, по нажатию на которую мы ожидаем, что произойдет сохранение значения переменной name в localStorage. Однако поведение программы будет несколько иное:

useEffect и useRef в React

Поскольку useEffect срабатывает в данном случае один раз, то соответственно он берет значение перемеенной name только один раз и никак не отслеживает ее изменения. Мы, конечно, могли бы передать в качестве параметра эту переменную name:

React.useEffect(() => {    
         // ....................
},
[name]); 	// эффект вызывается при каждом обновлении name

Но тогда бы useEffect вызывался при каждом изменении переменной name.

Теперь применим хук useRef:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel" data-type="module">
    import React from "https://esm.sh/react@19?dev";
    import ReactDOM from "https://esm.sh/react-dom@19/client?dev";
        
    const root = ReactDOM.createRoot(
         document.getElementById("app")
    );
    function UserForm() {
      const [name, setName] = React.useState("Tom");
      const nameRef = React.useRef(name);
       
      React.useEffect(() => { 
       
        nameRef.current = name;
      }, [name]);
       
      React.useEffect(() => {    
         // извлекаем данные из localStorage
         const userName = localStorage.getItem("userName");
         // если в localStorage есть такой объект
         if(userName!==null) {
            setName(userName);
            console.log("Got!");
        }
         
        // сохраняем данные в localStorage
        return()=>{
            console.log(nameRef.current);
            localStorage.setItem("userName", nameRef.current); 
            console.log("Saved!");
        }
      },
      []); // эффект срабатывает только один раз - при самом первом рендеринге
        
      const changeName = (event) => setName(event.target.value);
      const unmount =() => root.unmount();
       
      return (
        <div>
          <h3>Имя: {name}</h3>
            
          <div>
            <p>Имя: <input value={name} onChange={changeName} /></p>
            <button onClick={unmount}>Unmount</button>
          </div>
        </div>
      );
    }
             
    root.render(
        <UserForm />
    );
    </script>
</body>
</html>

Здесь вместе с состоянием компонента определяем ссылку nameRef:

const nameRef = React.useRef(name);

Ее начальное значение - это значение переменной name. И при каждом изменении переменной name соответственно меняем и значение в ссылке nameRef. Для этого определяем эффект с помощью хука useEffect:

React.useEffect(() => { 
	  
	nameRef.current = name;
}, [name]);

При этом данный эффект зависит от name, то есть срабатывает при любых изменениях значения name.

Основной хук useEffect, который сохраняет данные в LocalStorage, по прежнему запускается один раз - при первом рендеринге. Однако теперь мы сохраняем не значение переменной name, а значение в ссылке nameRef:

// сохраняем данные в localStorage
return()=>{
	console.log(nameRef.current);
	localStorage.setItem("userName", nameRef.current); 
	console.log("Saved!");
}

В отличие от переменной состояния name в useEffect, значение по ссылке nameRef будет изменяться, несмотря на то что, useEffect по-прежнему сработает только один раз

state и ref в useEffect и useRef в React

ref и классы-компоненты

В классах-компонентах мы естественно не можем использовать хук useRef(), однако для компонентов-классов React предоставляет другую похожую возможность - функцию React.createRef(). Эта функция, как и хук useRef(), позволяет создать ссылку, которую затем с помощью атрибута ref можно прикрепить к определенному элементу разметки. Рассмотрим небольшой пример:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel" data-type="module">
    import React from "https://esm.sh/react@19?dev";
    import ReactDOM from "https://esm.sh/react-dom@19/client?dev";
        
    class UserForm extends React.Component {
        constructor(props) {
            super(props);
            this.nameField = React.createRef();
        }
        send = ()=>{
            // свойство current указывает на элемент input
            const inputElement = this.nameField.current;
            console.log("Имя: ", inputElement.value);
        }
        render() {
            return <div>
                    <input defaultValue="Tom" ref={this.nameField} />
                    <button onClick={this.send}>Отправить</button>
                </div>;
        }
    }
    ReactDOM.createRoot(
        document.getElementById("app")
    )
    .render(
        <UserForm />
    );
    </script>
</body>
</html>

Для создания ссылок ref применяется функция React.createRef(). В данном случае это происходит в конструкторе:

this.nameField = React.createRef();

Затем созданную ссылку можно прикрепить к какому-нибудь элементу на html-странице. Для этого применяется применяется атрибут ref, которому в фигурных скобках передается ссылка.

<input ref={this.nameField} />

Далее в коде мы сможем ссылаться на этот элемент, в том числе получать его значения, с помощью выражения this.nameField.current.

Единственный минус в этом случае, то что мы не можем установить значение по умолчанию с помощью стандартного атрибута value: при использовании атрибута value опять же придется задавать обработчик события change, как в прошлой теме. Альтернативой данному подходу служит применение специального атрибута defaultValue, который задает для поля ввода значение по умолчанию:

render() {
	  return <div>
            <input defaultValue="Tom" ref={this.nameField} />
            <button onClick={this.send}>Отправить</button>
          </div>;
}
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850