j-snow
@j-snow
Java junior developer

Почему компонент рендерится дважды? Проблема в codesandbox?

Есть простое React-приложение:
let idCounter = 0;

export default function App() {
  const id = useMemo(() => {
    console.log("useMemo");
    return idCounter++;
  }, []);

  console.log("render", id);

  useEffect(() => {
    console.log("useEffect", id);
  });

  return id;
}

https://codesandbox.io/s/morning-bush-swky8

Вывод в консоль:
useMemo
render 0
useEffect 1


Почему в useEffect id стал равен 1 ?

Ощущение, что компонент полностью отрендерился 2 раза. Но тогда почему useMemo и useEffect не вызвались второй раз? Как id стал равным единице?
  • Вопрос задан
  • 3021 просмотр
Решения вопроса 1
Alexandroppolus
@Alexandroppolus
кодир
Эта хрень из-за < StrictMode > в index.ts. С ним начинает твориться какая-то абсолютно черная магия. Ещё и делается замена console.log, чтобы скрыть правду ))

https://codesandbox.io/s/suspicious-bohr-crvsh - используется сохраненная функция console.log, видно два рендера.

без < StrictMode > всё как ожидалось.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@TheOnlyFastCoder2
Может быть вы хотели сделать что то подобное ?
import React, { useState } from "react";

export default function App() {
  let [getID,setID] = useState(0);
  return (
    <>
      <p> {getID} </p>
      <button onClick={ () => setTimeout(() => setID(getID + 1),800)} > clickMe </button>
    </>
  )
}


или что то вроде этого

import React, { useState } from "react";

const MemoID = ({getID}) => {
  return Array.from(new Array(getID)).map( (_,i) => {
    return (<p key={i}> {i} </p>)
  })
}

export default function App() {
  let [getID,setID] = useState(0);
  return (
    <>
      <MemoID key={getID} {...{getID}}/>
      <button onClick={ () => setTimeout(() => setID(getID + 1),800)} > clickMe </button>
    </>
  )
}
Ответ написан
Комментировать
gscraft
@gscraft
Программист, философ
Во-первых, самое главное, все значения, которые используете в React, должны быть обернуты, т.к. нельзя контролировать в полной мере обновление компонента, особенно если он будет обернут в дерево (находиться внутри других компонент). Т.е. используйте значения только из useState, переданных параметров props. useMemo не сможет распознать иных зависимостей.

Во-вторых, useEffect должен использовать зависимости, чтобы не вызывался каждый раз. Если Вам нужно выполнение эффекта только при инициализации компонента, укажите useEffect(callback, []). useEffect без зависимостей практически бесполезен (и есть трюки, чтобы довести до однократного выполнения с помощью useRef).

В-третьих, если Вам нужно значение вне компонента, как хранилище, используйте прокси (передача значения сверху-вниз из родительских компонент в виде props), например, или другие инструменты (Redux, MobX).

Например,
import React from 'react';

function App(props) {
  return <a onClick={() => props.addIdCounter()}>{ props.idCounter }</a>
}

function RootComponent () {
  const [idCounter, setIdCounter] = React.useEffect(0);
  const addIdCounter = () => setIdCounter(idCounter + 1);

  return <App addIdCounter={addIdCounter} idCounter={idCounter} />;
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы