Если вы наткнулись на этот пост, скорее всего, вы уже знакомы с некоторыми из наиболее распространенных методов оптимизации React, такими как useCallback, useMemo и React.memo, чтобы уменьшить количество выполняемых ненужных повторных рендерингов; Библиотеки react-window и react-lazyload для ленивой загрузки большого количества элементов в списке/таблице или изображениях, чтобы избежать создания неиспользуемых элементов DOM за пределами экрана.

Но в этой статье мы сосредоточимся на некоторых менее известных методах оптимизации React (ну, по крайней мере, я не припоминаю, чтобы часто встречал их в Интернете).

  1. Ленивое начальное состояние
const [state, setState] = useState(expensiveFn());

Обычно, когда у вас есть функция с большими вычислительными затратами для возврата результата начального состояния, эта же функция будет выполняться для любого последующего повторного рендеринга, вызванного функцией диспетчеризации состояния.

Это неоптимально, потому что дорогостоящая функция нужна только для запуска для результата начального состояния, верно?

const [state, setState] = useState(() => expensiveFn());

Чтобы решить эту проблему, вы можете предоставить функцию стрелки, которая вместо этого запускает дорогостоящую функцию. Теперь React будет вызывать дорогостоящую функцию только во время первого рендеринга и создает новую функцию для последующих повторных рендерингов (обратите внимание, что мы больше НЕ запускаем дорогостоящую функцию внутри стрелочной функции, и создание новой функции намного дешевле, чем вызов того, что есть). внутри него).

const [state, setState] = useState(() => 'Hello World!');

Однако обратите внимание, что если начальное состояние не связано с какими-либо тяжелыми вычислениями, то можно не использовать ленивую инициализацию, поскольку повторное создание одного и того же значения во время повторного рендеринга не так дорого.

2. React.fragment, чтобы избежать добавления дополнительных тегов HTML

function Example() {
  return (
    <div>
      <h1>Hello</h1>
      <p>medium is the best!</p>
    </div>
  );
}

Функциональный компонент React может возвращать только один элемент HTML, поэтому иногда нам приходится добавлять новую пару тегов HTML (например, теги ‹div›), чтобы заключить все остальные элементы для выполнения правила React, однако это вводит еще один тег для повторного рендеринга.

Однако мы можем заменить дополнительные HTML-теги фрагментом React, который не вводит никаких дополнительных тегов для одновременного рендеринга и выполнения правила React.

Синтаксис выглядит следующим образом:

function Example() {
  return (
    <>
      <h1>Hello</h1>
      <p>medium is the best!</p>
    </>
  );
}

или альтернативно:

function Example() {
  return (
    <React.fragment>
      <h1>Hello</h1>
      <p>medium is the best!</p>
    </React.fragment>
  );
}

3. Государственное совместное размещение

Совместное размещение состояний в React означает сохранение состояний как можно ближе к тому месту, где они действительно необходимы, но какое это имеет отношение к оптимизации?

function Example() {
  const [text, setText] = useState(0);

  function onChange(e) {
    setText(e.target.value)
  }

  return (
    <>
      <SimpleInput text={text} onChange={onChange}  />
      <ExpensiveComponent />
    </>

  )

}

function SimpleInput({text, onChange}) {
  return (
    <>
      <input value={text} onChange={(e) => onChange(e)} />
    </>
  );
}

function ExpensiveComponent({}) {
  const expensiveFn = () => {
    for (let i = 0; i < 100000000; i++) {
      // nothing
    }
  };

  expensiveFn();

  return <div>slow</div>;
}

Возьмите приведенный выше код, например, каждый раз, когда состояние «текст» обновляется в родительском компоненте, React должен будет пройти через все свои дочерние компоненты (например, компоненты «SimpleInput» и «ExpensiveComponent»), чтобы проверить, нужны ли манипуляции с DOM. или нет, это значительно снижает производительность, если у вас есть супер дорогой компонент, такой как компонент «ExpensiveComponent».

Чтобы избежать ненужной проверки дорогостоящего дочернего компонента, мы можем разместить состояние «текст» в компоненте «SimpleInput», где оно действительно необходимо, как в примере кода ниже:

function Example() {
  return (
    <>
      <SimpleInput />
      <ExpensiveComponent />
    </>
  )
}

function SimpleInput({text, onChange}) {
  const [text, setText] = useState(0);

  function onChange(e) {
    setText(e.target.value)
  }

  return (
    <input value={text} onChange={(e) => onChange(e)} />
  );
}

function ExpensiveComponent({}) {
  const expensiveFn = () => {
    for (let i = 0; i < 100000000; i++) {
      // nothing
    }
  };

  expensiveFn();

  return <div>slow</div>;
}

Подведение итогов

Это суммирует 3 метода оптимизации React, надеюсь, вы узнали что-то сегодня, и любые предложения по улучшению приветствуются!