Вопрос или проблема
Здесь, в моем 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
в случае удаления рефа. Это означает, что, хотя коллбек срабатывает при изменениях, автоматическая очистка не выполняется так, как вы ожидаете.
Вот как вы можете обработать это:
- Проверяйте, когда
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
, срабатывает логика очистки.
- Однако, более стандартным и рекомендуемым способом выполнения очистки в 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
, поскольку функциональность рефов не обеспечивает автоматической очистки при ререндере.