Вариант использования для useLayoutEffect + useState против useMemo

Я видел такой ответ: useMemo против useEffect + useState, и он хорошо суммирует его для useEffect, но в моем случае я хочу выполнить дорогостоящую операцию, которая изменит DOM как можно раньше. Будет ли useMemo() вместо useLayoutEffect() с обновлением состояния? Отражает ли двойной рендеринг эффекта → обновление состояния какое-либо повышение производительности?

РЕДАКТИРОВАТЬ

useLayoutEffect():

useLayoutEffect(() => {
    const tokens = expensiveOperationGeneratingClasses(param1)
    setTokens(tokens)
}, 
[param1])

 render (
  <>
   {
       tokens.map(token => <span className={token.id}/>)
   }
  </>
 )

сценарий useMemo:

const tokens = useMemo(() => {
     return expensiveOperationGeneratingClasses(param1)
},
[param1]

 render (
  <>
   {
       tokens.map(token => <span className={token.id}/>)
   }
  </>
 )

На самом деле я понял, что я не выполняю DOM-операции, а просто генерирую имена классов перед рендерингом тегов <span> чтобы избежать мерцания, поэтому я думаю, что мне лучше использовать useMemo, я прав?

Ответ 1

Я постараюсь объяснить, где вы можете использовать LayoutEffect и Memo. Давайте начнем с использования LayoutEffect.

Использование LayoutEffect имеет некоторые недостатки, говорит Дэн Абрамов. Ссылка 1, Ссылка 2. Это хорошее объяснение того, где вы можете их использовать, дает Кент С. Доддс. Если вам нужен пример, вы можете увидеть его здесь, Крис. Не забывайте читать, чтобы понять разницу.

Теперь об использовании Memo. Это также имеет недостаток. Для чего мы используем Memo, и где он используется, вы можете найти здесь.

А сейчас на практике.

вариант 1 использовать LayoutEffect

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

import "./styles.css";
const Control = () => {
  const [add, setAdd] = useState(1);
  return (
    <div>
      <div>
        <PostOffice add={add} />
      </div>
      <div onClick={() => setAdd(add + 1)}>{"Click"}</div>
    </div>
  );
};

function PostOffice({ add }) {
  const [letter, setLetter] = useState(add);

  useLayoutEffect(() => {
    console.log("useLayoutEffect");
    setLetter(add);
  }, [add]);

  console.log(letter);
  return <div className="App">{console.log(letter, "DOM")}</div>;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Control />, rootElement);

Я не уверен в этом варианте 1, потому что здесь есть эффект анти-паттерна.

вариант 2 использовать LayoutEffect

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

import "./styles.css";
const Control = () => {
  const [add, setAdd] = useState(1);
  return (
    <div>
      <div>
        <PostOffice add={add} />
      </div>
      <div onClick={() => setAdd(add + 1)}>{"Click"}</div>
    </div>
  );
};

function PostOffice({ add }) {
  const [letter, setLetter] = useState(0);

  useLayoutEffect(() => {
    console.log("useLayoutEffect");
    setLetter(add);
  }, [add]);

  console.log(letter);
  return <div className="App">{console.log(letter, "DOM")}</div>;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Control />, rootElement);

будет бессмысленный рендеринг

опция useMemo

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

import "./styles.css";
const Control = () => {
  const [add, setAdd] = useState(1);
  return (
    <div>
      <div>
        <PostOffice add={add} />
      </div>
      <div onClick={() => setAdd(add + 1)}>{"Click"}</div>
    </div>
  );
};

function PostOffice({ add }) {
  const Letter = useMemo(() => {
    console.log("useMemo");
    return add + 1;
  }, [add]);

  console.log(Letter);
  return <div className="App">{console.log(Letter, "DOM")}</div>;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Control />, rootElement);

И здесь все работает отлично

Всего

Минус useMemo 1,

Минус useLayoutEffect, 1, эффект анти-паттерна или бессмысленный рендеринг, добавление useState,

Вот почему вы должны использовать useMemo.

но если есть способ не использовать эти крючки, это будет идеально.