Функция очистки обратного вызова Ref не выполняется

Вопрос или проблема

Здесь, в моем React, функция очистки не выполняется при повторном рендере:

import React, { useRef, useState } from 'react';

export default function App() {
  const [x, setX] = useState(2);
  const d = useRef(null);

  return (
    <>
      <p ref={(node) => {
        console.log('начало');
        return () => {
          console.log('конец');
        };
      }}>{x}</p>
      <button onClick={() => setX(x + 1)}>привет</button>
    </>
  );
}

Я хочу, чтобы моя функция очистки выполнялась, когда обновляется колбэк ref, поэтому здесь колбэк создается при каждом рендере, тогда очистка должна выполняться при каждом рендере!

Очистки рефов доступны только в сборках canary React, поэтому в настоящее время не доступны в стабильных версиях. Если вы хотите поэкспериментировать с этим, вам нужно использовать сборку canary на данный момент или проверить null, как предлагает Дрю в своем ответе:

function App() {
  const [x, setX] = React.useState(2);

   return (
      <React.Fragment>
        <p ref={(node) => {
          console.log('начало', node);
          return () => {
            console.log('конец', node);
          };
        }}>{x}</p>
        <button onClick={() => setX(x + 1)}>привет</button>
      </React.Fragment>
  );
}


const root = ReactDOM.createRoot(document.body);

root.render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.0-canary-c3048aab4-20240326/umd/react.development.min.js" integrity="sha512-N3bJHd0IQK3T+qeOxAxW+CC1/dYPREk4z6UvuqDrbk1DcK173oFL2RjSDwS1F5nLMaPwI3v4yWXCtuf2rYw3/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.0-canary-c3048aab4-20240326/umd/react-dom.development.min.js" integrity="sha512-36i1/IQRwia35jjTQr9i7Hsb/CaU8B5I0CL6/JMnOO7XExnCKXhsNR14c15DhSswOvN7KpH6qYAoCiJVvn3QBw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Рефы React не имеют функции очистки так, как это делает колбэк хука useEffect. Рефы React, установленные с помощью синтаксиса колбэков, просто вызываются снова и передаются null.

Пример:

function App() {
  const [x, setX] = React.useState(2);

  return (
    <div className="App">
      <span
        ref={(node) => {
          if (node) {
            console.log("начало", node);
          } else {
            console.log("конец", node);
          }
        }}
      >
        {x}
      </span>{" "}
      <button onClick={() => setX((x) => x + 1)}>+</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);

root.render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root" />

В колбэке рефа проверьте, когда реф равен null, и выполните любые «уборочные» задачи.

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

function App() {
  const [x, setX] = React.useState(2);

  React.useEffect(() => {
    console.log("начало");
    return () => {
      console.log("конец");
    };
  }, [x]);

  return (
    <div className="App">
      <span>
        {x}
      </span>{" "}
      <button onClick={() => setX((x) => x + 1)}>+</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);

root.render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root" />

Ответ или решение

В вашем коде проблема заключается в том, что функциональность очистки, которую вы хотите реализовать, не поддерживается в стабильных версиях React. Несмотря на то, что в вашем вопросе вы используете каллибек для рефа, этот механизм не вызывает очистку при каждом новом рендере.

В текущей версии React, когда вы используете коллбек для рефа, функция очистки не вызывается автоматически при обновлении компонента. Вместо этого React просто вызывает коллбек снова, передавая в него null в случае удаления рефа. Это означает, что, хотя коллбек срабатывает при изменениях, автоматическая очистка не выполняется так, как вы ожидаете.

Вот как вы можете обработать это:

  1. Проверяйте, когда node становится null для выполнения логики очистки.

Пример кода:

import React, { useState } from 'react';
import ReactDOM from 'react-dom';

function App() {
  const [x, setX] = useState(2);

  return (
    <div className="App">
      <p ref={(node) => {
        if (node) {
          console.log('start', node);
        } else {
          console.log('finish', node);
        }
      }}>
        {x}
      </p>
      <button onClick={() => setX((prev) => prev + 1)}>Increase</button>
    </div>
  );
}

const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);

В этом коде вы можете увидеть, что когда node становится null, срабатывает логика очистки.

  1. Однако, более стандартным и рекомендуемым способом выполнения очистки в React является использование хука useEffect. Это позволяет вам более четко управлять логикой жизненного цикла компонента.

Пример с использованием useEffect:

import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';

function App() {
  const [x, setX] = useState(2);

  useEffect(() => {
    console.log('start');
    return () => {
      console.log('finish');
    };
  }, [x]);

  return (
    <div className="App">
      <span>{x}</span>
      <button onClick={() => setX((prev) => prev + 1)}>Increase</button>
    </div>
  );
}

const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);

В этом примере мы используем useEffect, чтобы отследить изменения x, вызывая функцию очистки, когда x изменяется.

Таким образом, в текущей стабильной версии React, для реализации функции очистки рекомендуется использование useEffect, поскольку функциональность рефов не обеспечивает автоматической очистки при ререндере.

Оцените материал
Добавить комментарий

Капча загружается...