react: многократная инициализация внутри функционального компонента
Написал такой отладочный код:
import React, { useState } from 'react';
import { Button } from 'antd';
// компонент отображения страниц
const App: React.FC = () => {
const handleClick = () => {
dbgSetValue(old => old + 1);
}
// контроль состояний
const [dbg] = useState(console.log('dbg'));
const [dbgValue, dbgSetValue] = useState(0);
// отрисовать компонент
return (
<Button onClick={handleClick}>Жми</Button>
);
}
export default App;
каждый раз при нажатии на кнопку выводится dbg, т.е. вызывается useState.
у меня скорее всего не совсем точное понимание работы функциональных компонент, потому что я думал, что useState вызывается однократно при многократных перерисовках компонента, однако похоже это не так.
Отсюда вопрос:
Мне необходимо, чтобы вне зависимости от кол-ве отрисовок компонента внутри сохранялось бы некоторое значение. Как это лучше сделать?
Я думал, что смогу использовать useState для однократной инициализации такого значения, например если в качестве параметров передается некоторый класс, то я мог бы создать однократно ВНУТРИ компонента объект класса и работать с ним:
[target] = useState(new props.MyTarget)
Но очевидно такой подход не работает. Как же быть?
Мне очень не хотелось бы этот объект target делать внешним для данного компонента, хотелось бы однократно инициализировать внутри компонента и работать с ним вне зависимости что происходит с компонентом - какие состояния меняются, что делают дочерние компоненты и т.д.
Ответы (1 шт):
Вам надо почитать этот параграф
Суть в том, что значение в dbg не обновляется при каждом рендеринге, но функция сама внутри useState вызывается каждый при отрисовке компоненты. Значение функции устанавливается в dbg только в первый раз. Чтобы убедиться в этом можем создать useEffect, который будет вызываться при обновлении значения dbg:
const { useState, useEffect } = React;
const { createRoot } = ReactDOM;
const App = () => {
const handleClick = () => {
dbgSetValue(old => old + 1);
}
const [dbg] = useState(console.log('dbg'));
const [dbgValue, dbgSetValue] = useState(0);
useEffect(() => {
console.log('Новое знаечние dbg - это', dbg);
}, [dbg])
return (
<button onClick={handleClick}>Жми</button>
);
}
// Рендеринг
const container = document.querySelector('#root');
const root = createRoot(container);
root.render(<App />)
<div id="root"></div>
<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>
Но функция может очень трудоёмкой, и потому лучше всего в таких случаях передавать не результат выполнения функции, а саму функцию:
const { useState, useEffect } = React;
const { createRoot } = ReactDOM;
const App = () => {
const handleClick = () => {
dbgSetValue(old => old + 1);
}
const consoleLog1 = () => {
console.log('dbg1')
}
const consoleLog2 = () => {
console.log('dbg2')
}
const [dbg1] = useState(consoleLog1());
const [dbg2] = useState(consoleLog2);
const [dbgValue, dbgSetValue] = useState(0);
return (
<button onClick={handleClick}>Жми</button>
);
}
// Рендеринг
const container = document.querySelector('#root');
const root = createRoot(container);
root.render(<App />)
<div id="root"></div>
<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>