react: многократная инициализация внутри функционального компонента

Рейтинг: 1Ответов: 1Опубликовано: 23.04.2023

Написал такой отладочный код:

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>