October 7

REACT

Цель этой статьи

Целью этой статьи не является целенаправленное обучение этому фреймворку.

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

СОДЕРЖАНИЕ

1. Введение

2. React Hooks

2.1 Что представляют собой хуки в React, а также ключевые хуки и их возможности?
2.2 Назовите некоторые правила использования хуков.
2.3 Для чего нужен useCallback?
2.4 Каким образом будет осуществляться процесс рендеринга дочерних компонентов, передаваемых через map, с использованием useCallback из родительского компонента?
2.5 useMemo и useCallback, в чем их разница?
2.6 Чем отличается useMemo от React.memo?
2.7 Что передается вторым аргументом в React.memo?
2.8 Расскажите о хуках useRef и useState.
2.9 В чем разница между useState и useRef?
2.10 Чем отличается хранение состояния в useState и в useRef?
2.11 Расскажите о хуке useReducer. Какие функции он выполняет?
2.12 Сталкивались ли вы с хуком useLayoutEffect?
2.13 Чем отличается useEffect от useLayoutEffect?
2.14 Как воспроизвести функциональность классовых методов componentDidUpdate и componentDidMount в функциональных компонентах?
2.15 Что такое кастомный хук?
2.16 В React 18 появлось несколько новых хуков, какие?

3. useEffect

3.1 Расскажите о useEffect.
3.2 Когда вызывается callback из useEffect?
3.3 Когда вызывается useEffect?
3.4 Как с помощью useEffect эмулировать поведение componentDidUpdate?

4. useRef

4.1 Для чего нужен Ref в React?
4.2 Что такое useRef?
4.3 Типичный и нетипичный способы применения useRef.
4.4 Какова внутренняя реализация useRef в React и как она функционирует внутри компонента?

5. Компоненты

5.1 Что такое HOC?
5.2 styled components react, плюсы и минусы
5.3 React Portal
5.4 Как организуются компоненты?
5.5 Расскажите про процесс reconciliation в React
5.6 Компонент React Offscreen – зачем он нужен?
5.7 Что заставляет сделать ререндер компонента?
5.8 Что такое неконтролируемые компоненты?
5.9 Где в react в функциональном компоненте ты бы сделал обращение к API?

6. Состояние

6.1 Для чего используется Key
6.2 Для чего нужны state и props в React?
6.3 Расскажи про контекст в React
6.4 Ref и key, что это?
6.5 Почему не использовать встроенный контекст?
6.6 Как передать данные из дочернего в родительский
6.7 Что такое React.Context?
6.8. Что такое Prop drilling
6.9 Что будет если мы мутируем стейт, вместо того, чтобы выдавать новый объект?
6.10 Является ли верным такое использование свойства (prop) k
6.11 Как взаимодействовать с элементом через React, если требуется обратиться к нему?

7. Оптимизация

7.1 Оптимизации встроенными инструментами из React
7.2 Если мы применяем LazyLoading какие изменения появляются в сборке
7.3 Можно ли обернуть все переменные и функции в useMemo useCallback? В чем минусы
7.4 Каким образом можно достичь повторного использования общей логики между двумя компонентами в React?
7.5 Как увеличить производительность?
7.6 Перфоменс трюки в React
7.7 Что такое React.Lazy?
7.8 Если вы выполняете маппинг коллекции без указания явного ключа, какой ключ будет использоваться по умолчанию? Какой ключ будет использоваться для каждого компонента?
7.9 Зачем нужна функция lazy в React?

8. Общее

8.1 Что такое Shadow DOM
8.2 В чем разница между классовыми и функциональными компонентами
8.3 Что такое VirtualDOM
8.4 В чем разница между виртуальным DOM и обычным?
8.5 Что такое React? Зачем он нужен? Какие задачи решает?
8.6 Реакт быстрый из-за того, что он сравнивает реальный дом и виртуал дом. Можешь рассказать как это происходит?
8.7 В чем разница между NextJS и ReactJS
8.8 В чём отличие ReactDOM.render от ReactDOM.hydrate (или createRoot от hydrateRoot )?
8.9 Расскажи процесс рендеринга в React

9. Роутинг

9.1 хук useRoutes

ВВЕДЕНИЕ

REACT — это JavaScript-библиотека для создания пользовательских интерфейсов. Обратите внимание, что это именно библиотека, а не фреймворк. React часто называют фреймворком, но это ошибка. Во-первых, его использование ни к чему вас не обязывает, не формирует «фрейм» проекта. Во-вторых, React выполняет единственную задачу: показывает на странице компонент интерфейса, синхронизируя его с данными приложения, и только этой библиотеки в общем случае недостаточно для того, чтобы полностью реализовать проект.

Вскоре после появления React и подобные ему решения (Vue.js, Svelte) практически захватили мир фронтенда: потому что они помогают решать проблемы, основываясь на идее декларативного программирования, а не на императивном подходе.

Декларативный подход состоит в описании конечного результата (что мы хотим получить).

— При императивном подходе описываются конкретные шаги для достижения конечного результата (как мы хотим что-то получить).

Оказалось, что декларативный подход отлично подходит для создания интерфейсов, и он прижился в сообществе. Этот подход работает не только в вебе: сравнительно недавно компания Apple представила фреймворк SwiftUI, основанный на тех же принципах.

Но прежде чем изучать React, желательно уже на хорошем уровне владеть JS, HTML и CSS. Это ускорит освоение React, а также повысит ценность разработчика на рынке: потому что знание фундаментальных вещей помогает подобрать технологию, лучше всего подходящую для решаемой задачи — будь то React или что-то другое.

Особенности React

React — это не универсальный инструмент, который подойдёт для любого проекта. Чтобы понять, решит ли React ваши задачи, важно знать о его преимуществах и недостатках.

Эта библиотека действительно может упростить жизнь разработчикам:

— С её помощью можно построить интерфейс из отдельных компонентов, которые легко поддерживать.

— Она добавляет удобный слой абстракции, избавляя от необходимости работать с DOM напрямую.

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

— Благодаря сообществу у React хорошо проработанная документация и большой опыт, накопленный в статьях, курсах и конференциях. Это значительно облегчает не только изучение библиотеки новичками, но и поиск ответов на всевозможные вопросы в процессе работы.

— На Гитхабе можно найти готовые React-компоненты почти на все случаи жизни. А если их нет, но есть нужные независимые библиотеки, то можно поискать интеграцию либо сделать её самостоятельно.

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

— Хотя в проекте, скорее всего, будет налажен процесс сборки с помощью webpack, parcel, rollup или другого упаковщика, стоит иметь в виду, что это необязательно для использования React. Работая с этой библиотекой, вы пишете на чистом JS, и никакие диалекты HTML, CSS или JS изучать не нужно. Конечно, с React почти всегда используется JSX, но и это тоже опционально.

— React — это проект с открытым исходным кодом. Благодаря этому его можно безопасно использовать даже в коммерческих приложениях.

Однако при использовании React есть особенности, которые важно учитывать:

— React увеличивает размер приложения, которое нужно загрузить пользователям (~40 kB для пакетов React и React-dom).

— Загруженный код нужно выполнить в браузере: для пользователя это означает, что приложение будет запускаться медленнее.

— Вместе с виртуальным DOM возникают свои накладные расходы: во-первых, по времени выполнения (сравнение виртуальных деревьев происходит не моментально); во-вторых, по памяти, ведь виртуальные деревья нужно где-то хранить и не в единственном экземпляре. С увеличением количества элементов на странице эти расходы растут, что может стать реальной проблемой на мобильных устройствах. Поэтому при изучении React важно уделить внимание способам оптимизации рендеринга приложения – необходимые инструменты для этого есть в самой библиотеке.

— Средний порог входа в React. Чтобы начать разрабатывать на React, программист должен не только изучить саму библиотеку, но и привыкнуть к используемой парадигме.

Эти недостатки, конечно, не повод совсем отказаться от использования React и подобных ему библиотек при создании проектов. Но о них нужно помнить для того, чтобы использовать этот инструмент осознанно и понимая его ограничения.

Несмотря на то, что виртуальное дерево расходует дополнительные время и память, для большинства приложений это некритично: React остаётся достаточно быстрым и позволяет оптимизировать проблемные места там, где это нужно.

Что касается порога входа, то изучение React или подобных ему библиотек стоит рассматривать как инвестицию в себя, как в разработчика, потому что это актуальная, интересная и широко используемая технология.

Для каких проектов подойдёт React

Резюмируя все особенности, можно выделить несколько типов проектов, которым подойдет React.

1. Если проект планирует расширяться, и над ним работает или будет работать команда разработчиков. Потому что в таком случае использование известной технологии поможет проще договариваться между разработчиками и лучше поддерживать код.

2. Средним и крупным проектам будет полезен компонентный подход, который в том числе лежит в основах React. Это упростит структурирование и переиспользование кода и даст выигрыш в долгосрочной перспективе.

3. Legacy-проектам, которым предстоит пройти через рефакторинг. Потому что React можно добавлять в уже существующий проект, обновляя кодовую базу постепенно и точечно.

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

Ещё React будет не самым удачным выбором для реализации частей проекта, которые очень чувствительны к потребляемым ресурсам. В этом случае возможно гибридное использование: когда само приложение по большей части написано на React, а требовательные к производительности места с ним не взаимодействуют – библиотека никак не мешает делать такие интеграции.

НАЧНЕМ...

REACT HOOKS

Что представляют собой хуки в React, а также ключевые хуки и их возможности?

Хуки (hooks) в React - это новое API, представленное в React 16.8, которое позволяет функциональным компонентам использовать состояние (state) и другие возможности, ранее доступные только классовым компонентам

Хуки предоставляют специальные функции, называемые ключевыми хуками (built-in hooks), которые могут быть вызваны внутри функционального компонента для получения и использования определенных возможностей React.

Некоторые из ключевых хуков в React:

useState: Хук useState позволяет функциональному компоненту использовать состояние (state). Он возвращает пару значений - текущее состояние и функцию для его обновления. Пример использования: javascript

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

useEffect: Хук useEffect позволяет функциональному компоненту выполнять побочные эффекты, такие как выполнение запросов к API, подписка на события, изменение заголовка страницы и т. д. Он принимает функцию-эффект и, опционально, массив зависимостей, которые указывают, при изменении каких значений должен быть повторно вызван эффект. Пример использования:

import React, { useEffect, useState } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    setData(data);
  };

  return <div>{data ? <p>Data: {data}</p> : <p>Loading...</p>}</div>;
}

useContext: Хук useContext позволяет функциональному компоненту получить значение контекста, определенного в верхних компонентах и переданного с помощью Context.Provider. Он принимает объект контекста и возвращает текущее значение контекста. Пример использования: javascript

import React, { useContext } from 'react';
import MyContext from './MyContext';

function MyComponent() {
  const contextValue = useContext(MyContext);

  return <div>{contextValue}</div>;
}

Кроме этих ключевых хуков, в React также есть другие встроенные хуки, такие как useReducer для управления сложным состоянием, useCallback для оптимизации колбэк-функций, useMemo для оптимизации вычислений, useRef для получения ссылок на DOM-элемент и другие хуки, предоставляющие различные возможности.

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

Некоторые правила использования хуков

Да, есть несколько правил, которые следует соблюдать при использовании хуков в React:

  • Используйте хуки только в функциональных компонентах. Хуки не могут быть использованы в классовых компонентах.
  • Избегайте использования хуков внутри других хуков. Например, с осторожностью используйте хук useState внутри хука useEffect, чтобы не вызвать бесконечный цикл.
  • Используйте хуки только в тех местах, где это необходимо. Например, не используйте хук useCallback для оборачивания каждой функции
  • Используйте хуки с умеренностью. Не используйте большое количество хуков в одном компоненте, чтобы избежать ухудшения производительности.
  • Используйте хуки правильно. Например, используйте хук useMemo для кэширования значений, а не хук useCallback.
  • Тестируйте свой код. Используйте тесты для проверки корректности работы вашего кода и избежания ошибок.
  • Обновляйте свой код. Используйте новые версии библиотек и фреймворков, чтобы получить доступ к новым хукам и функциям.

Для чего нужен useCallback?

useCallback - это хук в React, который позволяет оптимизировать производительность компонентов путем кеширования колбэк-функций.

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

useCallback решает эту проблему, сохраняя колбэк-функцию между рендерами компонента, если ее зависимости не изменились. Таким образом, при повторном рендеринге компонента будет использоваться одна и та же функция, а не создаваться новая. Это позволяет избежать ненужных перерисовок дочерних компонентов, которые принимают эти колбэк-функции.

Пример использования useCallback:

import React, { useCallback } from 'react';

function ParentComponent() {
  const handleClick = useCallback(() => {
    // Логика обработки клика
    console.log('Button clicked');
  }, []);

  return <ChildComponent onClick={handleClick} />;
}

function ChildComponent({ onClick }) {
  // Рендеринг дочернего компонента
  return <button onClick={onClick}>Click me</button>;
}

В этом примере handleClick будет кеширована с помощью useCallback. При каждом рендере ParentComponent будет использоваться одна и та же функция handleClick, если ее зависимости (в данном случае, пустой массив зависимостей) не изменились. Это позволяет передавать стабильные колбэк-функции в дочерние компоненты, минимизируя перерисовки этих компонентов при обновлении родительского компонента.

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

Каким образом будет осуществляться процесс рендеринга дочерних компонентов, передаваемых через map, с использованием useCallback из родительского компонента?

В React процесс рендеринга дочерних компонентов, которые возвращает метод map, может быть оптимизирован с использованием useCallback в родительском компоненте. useCallback позволяет сохранить ссылку на колбэк-функцию и повторно использовать ее при повторном рендеринге родительского компонента. Это особенно полезно, когда дочерние компоненты используют эту функцию в качестве пропса, чтобы избежать ненужных повторных рендеров дочерних компонентов.

Вот пример кода, демонстрирующий этот процесс:

import React, { useCallback } from 'react';

function ParentComponent() {
  const items = ['item 1', 'item 2', 'item 3'];

  const handleClick = useCallback((item) => {
    console.log('Clicked:', item);
  }, []);

  return (
    <div>
      {items.map((item) => (
        <ChildComponent key={item} item={item} onClick={handleClick} />
      ))}
    </div>
  );
}

function ChildComponent({ item, onClick }) {
  console.log('Rendering:', item);
  return <button onClick={() => onClick(item)}>Click me</button>;
}
В этом примере handleClick обернут в useCallback, и передается в дочерний компонент ChildComponent через пропс onClick. Таким образом, при повторном рендеринге ParentComponent, колбэк-функция handleClick будет повторно использоваться, и дочерние компоненты не будут перерисовываться, если пропсы, переданные им, не изменились.

Использование useCallback особенно полезно, когда родительский компонент имеет большое количество дочерних компонентов или когда функция требует значительных вычислительных затрат.

useMemo и useCallback, в чем их разница?

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

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

Вот пример использования useMemo:

const memoizedValue = useMemo(() => 
computeExpensiveValue(a, b), [a, b]);

В этом примере computeExpensiveValue - это функция, которая выполняет сложные вычисления. Если значения a или b изменятся, useMemo выполнит computeExpensiveValue и вернет новое значение. В противном случае будет возвращено закэшированное значение.

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

Вот пример использования useCallback:

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

В этом примере doSomething - это функция, которую вы хотите кэшировать. Если значения a или b изменятся, useCallback вернет новую функцию. В противном случае будет возвращена закэшированная функция.

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

Чем отличается useMemo от React.memo?

useMemo и React.memo - это две разные вещи, которые используются для оптимизации производительности компонентов React.

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

React.memo - это компонент высшего порядка (HOC), который позволяет оптимизировать рендеринг компонентов. Он принимает компонент, который нужно оптимизировать, и массив зависимостей. Если зависимости не изменились, то компонент не будет перерендериваться. Вместо этого, результат предыдущего рендеринга будет использован.

Основное отличие между useMemo и React.memo заключается в том, что useMemo вычисляет значение при изменении зависимостей, а React.memo оптимизирует рендеринг компонентов. Если вы хотите оптимизировать рендеринг компонентов, то React.memo может быть полезным. Если же вы хотите вычислять значение только при изменении зависимостей, то useMemo может быть более эффективным.

Что передается вторым аргументом в React.memo?

В React функция React.memo() используется для оптимизации производительности компонентов путем предотвращения ненужного повторного рендеринга. Эта функция создает мемоизированную версию компонента, которая будет повторно рендериться только при изменении пропсов.

Второй аргумент в функции React.memo() - это функция сравнения (также называемая "компаратором"). Эта функция определяет, должны ли новые пропсы считаться равными предыдущим пропсам, чтобы компонент не перерисовывался.

Сравнение пропсов в React.memo() происходит следующим образом:

  • Если второй аргумент не указан, React.memo() будет использовать поверхностное сравнение пропсов. Это означает, что он будет сравнивать ссылки на объекты и значения примитивов. Если ссылки или значения равны, компонент не будет перерисован.
  • Если второй аргумент - это функция сравнения, то при каждом рендере React будет вызывать эту функцию, передавая текущие пропсы и предыдущие пропсы. Если функция вернет true, компонент не будет перерисован.

Пример использования React.memo() с функцией сравнения:

import React from 'react';

const MyComponent = React.memo((props) => {
  // Компонентов не будет перерисовываться,
  // если пропсы равны по значению свойств "a" и "b"
}, (prevProps, nextProps) => {
  return prevProps.a === nextProps.a && prevProps.b === nextProps.b;
});

Таким образом, второй аргумент в React.memo() позволяет более точно контролировать, когда компонент должен быть перерисован на основе изменений пропсов.

Расскажите о хуках useRef и useState.

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

Пример использования useState:

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

useRef - это хук, который позволяет создавать ссылки на DOM-элементы или на другие значения, которые могут сохраняться между рендерами компонента без вызова перерисовки. Ссылки, созданные с помощью useRef, могут быть изменены без вызова рендера компонента.

Пример использования useRef для доступа к DOM-элементу:

import React, { useRef, useEffect } from 'react';

function FocusableInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} />;
}

Разница между useState и useRef:

  • useState используется для управления состоянием компонента и вызывает перерисовку при изменении состояния. Состояние, управляемое useState, может быть привязано к элементам интерфейса.
  • useRef используется для сохранения изменяемых значений между рендерами, и изменение значения, хранящегося в useRef, не вызывает перерисовку компонента. Он часто используется для работы с DOM-элементами или для хранения данных, которые не должны вызывать перерисовку.

Важно отметить, что хотя useRef также может использоваться для доступа к DOM-элементам, лучше всего использовать ref напрямую в JSX при работе с элементами. useRef ценен в случаях, когда вам нужно сохранить изменяемое значение, и это значение не обязательно должно влиять на рендеринг компонента.

В чем разница между useState и useRef?

Разница между useState и useRef в React заключается в их предназначении и поведении:

useState:

    • Предназначение: useState - это хук (hook) в React, который используется для добавления состояния в функциональные компоненты. Он позволяет объявлять переменные состояния, которые могут изменяться в процессе работы компонента и вызывают повторный рендеринг компонента при изменении значения.
    • Сохранение состояния: Каждый вызов useState создает переменную состояния и связанный с ней setter-метод, который позволяет изменять состояние компонента и запускать повторный рендеринг. Обновление состояния вызывает повторный вызов функционального компонента.

Пример использования useState:

import React, { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  const handleIncrement = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Счетчик: {count}</p>
      <button onClick={handleIncrement}>Увеличить счетчик</button>
    </div>
  );
}

Пример использования useRef:

import React, { useRef, useEffect } from 'react';

function MyComponent() { const inputRef = useRef(null);

useEffect(() => { // Фокусируемся на поле ввода при монтировании компонента inputRef.current.focus(); }, []);

return ( <div> <input ref={inputRef} type="text" /> </div> ); }

Итак, основная разница между useState и useRef состоит в их предназначении: useState используется для управления состоянием компонента и вызывает повторный рендеринг, а useRef используется для хранения данных, которые не требуют повторного рендеринга компонента.

Чем отличается хранение состояния в useState и в useRef?

В React компоненте useState и useReducer могут привести к тому, что ваш компонент будет перерисовываться при каждом вызове функций обновления.

Хук useRef() можно использовать для отслеживания переменных, не вызывая повторного рендеринга. Переменная Ref в React — это изменяемый объект, но его значение сохраняется в React при всех повторных рендерингах. Объект ref имеет единственное свойство current, благодаря чему ref имеет структуру, подобную { current: ReactElementReference}. Часто ref используют для хранения ссылок на DOM элементы, для реализации уникального поведения этих елементов (например получение фокуса).

Уникальный способ применения хука useRef — использовать его для хранения значений вместо ссылок DOM. Эти значения могут быть либо состоянием, которое не нужно менять слишком часто, либо состоянием, которое должно меняться как можно чаще, но не должно вызывать полного перерисовки компонента.

В React существует два механизма рендеринга: shallow и deep. Поверхностный рендеринг (shallow) затрагивает только компонент, но не его дочерние элементы, в то время как **глубокий рендеринг (deep) **затрагивает сам компонент и все его дочерние элементы.

После обновления значение useRef обновляется, но дочерние компоненты не обновляют свой пользовательский интерфейс, поскольку они не перерисовываются. Это поверхностный рендеринг, и именно он реализован для хука useRef.

Глубокий рендеринг используется при обновлении состояния с помощью хука useState.

Расскажите о хуке useReducer. Какие функции он выполняет?

useReducer - это хук в React, который позволяет управлять состоянием компонента. Он принимает два аргумента: функцию-редьюсер и начальное состояние компонента. Функция-редьюсер принимает текущее состояние компонента и действие, которое нужно выполнить, и возвращает новое состояние компонента.

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

useReducer также может быть полезен при работе с асинхронными действиями, так как он позволяет управлять состоянием компонента в асинхронном режиме.

Пример использования:

import React, { useReducer } from "react";
const initialState = { count: 0 };
function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const increment = () => {
    dispatch({ type: "increment" });
  };
  const decrement = () => {
    dispatch({ type: "decrement" });
  };
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={increment}>Increment</button>{" "}
      <button onClick={decrement}>Decrement</button>
    </div>
  );

Сталкивались ли вы с хуком useLayoutEffect?

Хук useLayoutEffect похож на useEffect в React, и хотя оба хука позволяют выполнять побочные эффекты в функциональных компонентах, useLayoutEffectони срабатывают синхронно после всех мутаций DOM, но до того, как браузер сможет отобразить эти изменения на экране. Это может быть полезно, когда нужно прочитать информацию о макете из DOM или выполнить действия, требующие синхронных обновлений.

Синтаксис использования useLayoutEffect такой же, как у useEffect:

import React, { useLayoutEffect } from 'react';

function MyComponent() {
  useLayoutEffect(() => {
    // Your side-effect code here
    // This code will run after the component has rendered but before the browser paints the changes on the screen
    // It's a good place for DOM measurements or other imperative actions
    
    return () => {
      // Cleanup code here (optional)
    };
  }, []); // Dependency array

  // Render your component
  return (
    // JSX code
  );
}

Вот несколько важных моментов, которые следует учитывать при использовании useLayoutEffect:

Время: useLayoutEffect запускается синхронно сразу после того, как React выполнил все мутации DOM. Это потенциально может заблокировать отрисовку и сделать приложение менее отзывчивым, если код внутри хука выполняется долго.

Массив зависимостей: как и в случае с useEffect, вы можете указать массив зависимостей в качестве второго аргумента для useLayoutEffect. Он определяет, когда эффект должен работать. Если массив зависимостей пуст ([ ]), эффект запустится только один раз после начального рендеринга. Если вы предоставляете зависимости, эффект будет выполняться при каждом изменении любой из зависимостей.

useLayoutEffect ведет себя аналогично методам жизненного цикла componentDidMount и componentDidUpdate в компонентах класса. Однако из-за его синхронного характера обычно рекомендуется использовать useEffectвместо этого, если только вам не нужно специально взаимодействовать с DOM перед отрисовкой браузера.

Рендеринг на стороне сервера (SSR): Если вы используете рендеринг на стороне сервера, это useLayoutEffectможет привести к некоторым предупреждениям или ошибкам. В этом случае вы можете использовать его useEffectвместо этого, так как он выполняется асинхронно в браузере и не блокирует отрисовку сервера.

useLayoutEffect необходимо использовать с осторожностью и только тогда, когда действительно нужно выполнять связанные с макетом или синхронные задачи. В большинстве случаев useEffect является предпочтительным вариантом.

Чем отличается useEffect от useLayoutEffect?

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

  • useEffect выполняется после того, как компонент был отрисован в DOM и после каждого обновления. Он используется для выполнения побочных эффектов, таких как загрузка данных из API, подписка на события или изменение DOM. useEffect принимает два аргумента: функцию эффекта и массив зависимостей. Функция эффекта выполняет побочный эффект, а массив зависимостей определяет, когда эффект должен быть выполнен. Если массив зависимостей пустой, то эффект будет выполнен только один раз после первого рендеринга компонента.
  • useLayoutEffect выполняется сразу после того, как браузер получил информацию о размерах и положении элементов на странице. Он используется для выполнения побочных эффектов, которые зависят от размеров и положения элементов на странице, таких как анимация или изменение стилей. useLayoutEffect принимает те же два аргумента, что и useEffect , но он выполняется сразу после того, как браузер получил информацию о размерах и положении элементов на странице.
Обратите внимание, что useEffect может вызывать бесконечный цикл, если зависимости не определены правильно. Поэтому вместо useEffect следует использовать useLayoutEffect , если побочный эффект зависит от размеров и положения элементов на странице.

Таким образом, useEffect используется для выполнения побочных эффектов после того, как компонент был отрисован в DOM и после каждого обновления, а useLayoutEffect используется для выполнения побочных эффектов сразу после того, как браузер получил информацию о размерах и положении элементов на странице.

Как воспроизвести функциональность классовых методов componentDidUpdate и componentDidMount в функциональных компонентах?

componentDidUpdate: Код метода componentDidUpdate выполняется, когда компонент обновляется и перерисовывается. Для аналогичной функциональности можно использовать хук useEffect с не пустым массивом зависимостей. Это позволит выполнить эффект после монтирования компонента и после каждого обновления.

import React, { useEffect } from 'react';

function FunctionalComponent() {
useEffect(() => {
  // Здесь размещается код для выполнения побочного эффекта
  // ...

  // Функция очистки, выполняющаяся перед размонтированием компонента
  return () => {
    // Здесь размещается код для очистки побочного эффекта
    // ...
  };
}, [dependency1, dependency2]);
}

componentDidMount: Метод componentDidMount вызывается после того, как компонент был добавлен в DOM. В функциональных компонентах аналогичную функциональность можно достичь с помощью хука useEffect с пустым массивом зависимостей. Этот хук будет выполнять код эффекта после монтирования компонента.

import React, { useEffect } from 'react';

function FunctionalComponent() {
  useEffect(() => {
    // Код, который будет выполнен после монтирования компонента
    // Этот код аналогичен componentDidMount
  }, []);

  return (
    // JSX вашего компонента
  );
}

Что такое кастомный хук?

Кастомный хук - это функция, которая выносит логику компонента в отдельную единицу для повторного использования.

Основное отличие кастомных хуков от обычных функций заключается в использовании внутри них стандартных хуков React, таких как useState, useEffect и т.д. Кастомные хуки, подобно стандартным, предназначены для использования внутри функциональных компонентов.

Вот пример хука useInput, который содержит логику обновления значения value в теге input. Этот хук позволяет вынести логику в отдельную функцию, что делает код чище и понятнее, и не загромождает его повторениями:

import React, { useState } from 'react';

function useInput(initialValue) {
    const [value, setValue] = useState(initialValue);
    function handleChange(e) {
        setValue(e.target.value);
    }
    return [value, handleChange];
}

export default useInput;

При написании кастомных хуков нужно придерживаться следующих правил:

  1. Кастомный хук должен начинаться с префикса use;
  2. Кастомные хуки должны избегать прямой работы с DOM элементами. Вместо этого, используйте useRef для управления элементами DOM;
  3. Кастомные хуки должны вызываться только на верхнем уровне функционального компонента, как и стандартные хуки;
  4. Возвращайте массив или объект из кастомных хуков, как и в стандартных хуках. Если возвращается одно значение, массив или объект необязательны. Если возвращается два значения, массив будет достаточным. Если возвращается больше двух значений, лучше использовать объект.

В React 18 появлось несколько новых хуков, какие?

В новом React 18 появилось несколько новых хуков:

  • useSyncExternalStore: Этот хук позволяет синхронизировать данные между клиентом и сервером. Он может быть полезен, если вы хотите обновлять данные на странице без перезагрузки.
  • useInsertionEffect: Этот хук позволяет выполнить эффект после вставки элемента в DOM. Он может быть полезен, если вы хотите выполнить какой-то код после вставки элемента в DOM.
  • useDeferredValue : Этот хук позволяет отложить обновление значения до тех пор, пока не будет готово клиентское приложение. Он может быть полезен, если вы хотите ускорить загрузку страницы, не дожидаясь завершения загрузки всех данных.
  • useOpaqueIdentifier : Этот хук позволяет создавать неопознанные идентификаторы, которые могут быть использованы для дедупликации элементов. Он может быть полезен, если вы хотите создавать компоненты, которые могут быть использованы несколько раз на странице.
  • useTransition : Этот хук позволяет управлять анимациями в компонентах React. Он может быть полезен, если вы хотите создавать анимации, которые не будут блокировать рендеринг страницы.

useEffect

Расскажите о useEffect.

useEffect - это хук в библиотеке React, который позволяет выполнять побочные эффекты в функциональных компонентах. Он позволяет выполнять определенные действия после рендеринга компонента, такие как подписка на события, получение данных с сервера, изменение DOM и многое другое.

Основной синтаксис useEffect

useEffect(() => {
  // Здесь размещается код для выполнения побочного эффекта
  // ...

  // Функция очистки, выполняющаяся перед размонтированием компонента
  return () => {
    // Здесь размещается код для очистки побочного эффекта
    // ...
  };
}, [dependency1, dependency2]);

Получение данных с сервера

useEffect(() => {
  const fetchData = async () => {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    // Обработка полученных данных
  };

  fetchData();
}, []);

Подписка на события

useEffect(() => {
  const handleClick = () => {
    // Обработка клика
  };

  window.addEventListener('click', handleClick);

  // Функция очистки подписки
  return () => {
    window.removeEventListener('click', handleClick);
  };
}, []);

Массив зависимостей

Когда компонент монтируется или перерендеривается, код внутри useEffect выполняется. Вы можете определить зависимости в виде массива (второй аргумент), и useEffect будет запускаться только при изменении указанных зависимостей. Если массив зависимостей пустой, то useEffect будет выполняться только один раз после монтирования компонента.

Когда вызывается callback из useEffect?

Callback из useEffect вызывается после каждого рендера компонента, если useEffect не имеет зависимостей (dependencies), или после каждого обновления зависимостей, если они есть.

Если useEffect не имеет зависимостей:

import React, { useEffect } from 'eact';

function MyComponent() {
  useEffect(() => {
    console.log('Component rendered');
  });

  return <div>Hello, world!</div>;
}

В этом примере useEffect вызывается после каждого рендера компонента и выводит сообщение в консоль.

Если useEffect имеет зависимости:

import React, { useEffect } from 'eact';

function MyComponent({ count }) {
  useEffect(() => {
    console.log('Count changed:', count);
  }, [count]);

  return <div>Count: {count}</div>;
}

В этом примере useEffect вызывается после каждого обновления зависимости count и выводит сообщение в консоль с новым значением count.

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

import React, { useEffect } from 'eact';

function MyComponent() {
  useEffect(() => {
    console.log('Component mounted');
    return () => {
      console.log('Component unmounted');
    };
  }, []);

  return <div>Hello, world!</div>;
}

В этом примере useEffect вызывается только один раз после первого рендера компонента и выводит сообщение в консоль. Затем useEffect возвращает функцию-компонент, которая вызывается при размонтировании компонента и выводит сообщение в консоль.

Когда вызывается useEffect?

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

Общий жизненный цикл useEffect выглядит следующим образом:

  • Первоначальный рендер: useEffect вызывается сразу после первоначального рендера компонента. Это происходит после того, как компонент отобразился на экране. В этом случае useEffect выполняется без каких-либо зависимостей.
  • Обновление компонента: Если указаны зависимости в массиве зависимостей (второй аргумент useEffect), useEffect вызывается только при изменении значений этих зависимостей. Если какая-либо из зависимостей изменилась по сравнению с предыдущим рендером, useEffect будет вызван снова. Если массив зависимостей пустой, useEffect вызывается только после первоначального рендера и больше не будет вызываться во время обновлений компонента.
  • Очистка (Cleanup): Если внутри useEffect возвращается функция, эта функция будет вызвана перед следующим вызовом useEffect или перед размонтированием компонента. Она может использоваться для отмены подписок, очистки ресурсов или выполнения других необходимых действий перед удалением компонента.

Пример использования useEffect:

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // Этот код будет выполнен после каждого рендера компонента
    console.log('useEffect called');
    
    // Функция cleanup будет вызвана перед следующим вызовом useEffect или перед размонтированием компонента
    return () => {
      console.log('Cleanup function called');
    };
  }, [dependency]);

  return <div>My Component</div>;
}

В этом примере useEffect будет вызываться после каждого рендера компонента, только если dependency изменилась. Если dependency не изменится, useEffect не будет вызываться повторно. Когда компонент размонтируется, функция очистки (cleanup) будет вызвана.

Замечание: Если массив зависимостей не указан, useEffect будет вызываться после каждого рендера компонента.

Как с помощью useEffect эмулировать поведение componentDidUpdate?

Для эмуляции поведения componentDidUpdate с помощью useEffect можно использовать зависимости между useEffect и componentDidUpdate.

Например, если вы хотите выполнить какой-то код при обновлении компонента, вы можете добавить useEffect с зависимостью от props или state. Когда эти значения изменятся, useEffect `будет вызван, и вы сможете выполнить нужные действия.

Вот пример кода, который эмулирует поведение componentDidUpdate с помощью useEffect:

import React, { useEffect, useState } from 'eact';

function MyComponent(props) {
  const [count, setCount] = useState(0);

  useEffect(() => {
    // Этот код будет выполнен при каждом обновлении компонента
    console.log('Component updated');
  }, [props, count]);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

В этом примере мы добавили useEffect с зависимостью от props и count. Когда эти значения изменятся, useEffect будет вызван, и мы сможем выполнить нужные действия. В данном случае мы просто выводим сообщение в консоль при каждом обновлении компонента.

useRef

Для чего нужен Ref в React?

Ref в React - это механизм, который позволяет вам получать доступ к DOM-элементам или другим компонентам в функциональных компонентах. Он также может использоваться для сохранения изменяемых значений между рендерами компонента. Ref обеспечивает способ взаимодействия с элементами интерфейса или другой информацией вне обычного потока обновления компонента.

Вот несколько основных сценариев использования Ref:

  1. Доступ к DOM-элементам: Ref позволяет получать доступ к реальным DOM-элементам, созданным React, и взаимодействовать с ними напрямую. Это может быть полезно для установки фокуса на элемент, измерения его размеров или выполнения других DOM-манипуляций.
  2. Сохранение состояния между рендерами: Ref также может использоваться для сохранения значений, которые не должны вызывать перерисовку компонента при их изменении. Например, вы можете использовать Ref для хранения предыдущего значения состояния или для сохранения других вспомогательных данных.
  3. Передача функциональности в дочерние компоненты: Вы можете использовать Ref для передачи методов или функций в дочерние компоненты, чтобы дать им возможность взаимодействовать с родительским компонентом или друг с другом.

Пример использования Ref для доступа к DOM-элементу:

import React, { useRef, useEffect } from 'react';

function MyComponent() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} />;
}

Пример использования Ref для сохранения изменяемого значения:

import React, { useState, useRef } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();

  useEffect(() => {
    prevCountRef.current = count;
  });

  const prevCount = prevCountRef.current;

  return (
    <div>
      <p>Current Count: {count}</p>
      <p>Previous Count: {prevCount}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Ref следует использовать с осторожностью, так как он позволяет обходить обычный поток данных в React и может усложнить отладку и поддержку кода.

Что такое useRef?

useRef - это хук (hook) в React, который позволяет получить доступ к DOM-элементам или хранить переменные, которые будут сохранять свои значения между рендерами компонента.

Основное предназначение useRef:

  1. Получение доступа к DOM-элементам: Вы можете использовать useRef для получения ссылки на DOM-элемент и взаимодействия с ним напрямую, без необходимости обращаться к DOM через document.querySelector или другие методы.
  2. Сохранение переменных между рендерами: Значение, которое хранится в useRef, сохраняется между рендерами компонента, и изменения значения не приведут к повторному рендерингу компонента.

Примеры использования useRef:

  1. Получение ссылки на DOM-элемент:
import React, { useRef, useEffect } from 'react';

function MyComponent() {
  const inputRef = useRef(null);

  useEffect(() => {
    // Фокусируемся на поле ввода при монтировании компонента
    inputRef.current.focus();
  }, []);

  return (
    <div>
      <input ref={inputRef} type="text" />
    </div>
  );
}
  1. Сохранение переменных между рендерами:
import React, { useState, useRef } from 'react';

function MyComponent() {
  const countRef = useRef(0); // Используем useRef для сохранения значения

  const handleButtonClick = () => {
    countRef.current++; // Изменяем значение без вызова rerender
    console.log('Current count:', countRef.current);
  };

  return (
    <div>
      <button onClick={handleButtonClick}>Увеличить счетчик</button>
    </div>
  );
}

Обратите внимание, что useRef обновляется без вызова повторного рендеринга компонента, поэтому изменение значения useRef не вызовет обновления компонента, в отличие от обновления состояния через useState.

Типичный и нетипичный способы применения useRef.

useRef() возвращает изменяемый ref-объект, свойство .current которого инициализируется переданным аргументом (initialValue). Возвращённый объект будет сохраняться в течение всего времени жизни компонента. По сути, useRef похож на «коробку», которая может содержать изменяемое значение в своём свойстве .current.

Типичный случай использования — это доступ к потомку в императивном стиле:

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` указывает на смонтированный элемент `input`
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Установить фокус на поле ввода</button>
    </>
  );
}

Хук useRef() может использоваться не только для DOM-рефов. Реф — это общий контейнер, а его свойство current — изменяемое и может хранить любое значение, подобно свойству экземпляра класса. Например:

function Timer() {
  const intervalRef = useRef();

  useEffect(() => {
    const id = setInterval(() => {
      // ...
    });
    intervalRef.current = id;
    return () => {
      clearInterval(intervalRef.current);
    };
  });

  // ...
}

  function handleCancelClick() {
    clearInterval(intervalRef.current);
  }
  // ...

В этом примере мы храним id setInterval в поле .current рефа для сброса в функции handleCancelClick()

Какова внутренняя реализация useRef в React и как она функционирует внутри компонента?

Внутренняя реализация useRef в React достаточно интересна. Она использует объект, называемый "мутабельный объект" (mutable object), чтобы обеспечить сохранение изменяемых значений между рендерами компонента без вызова перерисовки. По сути, useRef использует ту же ссылку на объект между рендерами, не вызывая перерисовку компонента при изменении значения.

Вот как примерно работает внутренняя реализация useRef:

  1. Первый рендер компонента: При первом рендере компонента вызывается функция useRef(). Внутри useRef создается мутабельный объект, который содержит свойство current, указывающее на переданное начальное значение (или undefined, если ничего не передано).
  2. Обращение к current: Вы можете обращаться к свойству current объекта, возвращенного useRef, чтобы получить или изменить значение. Например, inputRef.current возвращает текущее значение ссылки на DOM-элемент или другое значение.
  3. Изменение значения: Если вы измените значение свойства current в мутабельном объекте, это изменение не вызовет перерисовку компонента. React будет сохранять ссылку на этот объект между рендерами.
  4. Переиспользование ссылки: При следующих рендерах компонента, когда вызывается useRef() снова, React возвращает тот же самый мутабельный объект. Это означает, что inputRef будет указывать на тот же объект, что и в предыдущем рендере.
  5. Завершение компонента: Если компонент уничтожается (например, при смене маршрута), React сохраняет ссылку на мутабельный объект для использования в будущем, если компонент будет снова создан. Это позволяет сохранять состояния и значения между разными экземплярами компонента.

Ключевой момент заключается в том, что изменение значения свойства current не вызывает перерисовку, и компонент может получать доступ к этому значению без создания нового рендера. Это делает useRef удобным инструментом для хранения изменяемых данных между рендерами, а также для взаимодействия с DOM-элементами без нарушения принципов React.

КОМПОНЕНТЫ

Что такое HOC?

HOC (Higher-Order Component) - это паттерн высшего порядка в React, который позволяет переиспользовать логику компонентов и расширять их функциональность. HOC - это функция, которая принимает компонент и возвращает новый компонент.

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

Пример использования HOC может быть следующим:

function withLogger(WrappedComponent) {
  return class extends React.Component {
    componentDidMount() {
      console.log('Компонент был отрисован');
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

// Компонент, к которому мы хотим добавить логирование
class MyComponent extends React.Component {
  render() {
    return <div>Привет, мир!</div>;
  }
}

// Оборачиваем компонент с помощью HOC
const EnhancedComponent = withLogger(MyComponent);

В приведенном выше примере withLogger является HOC. Он принимает компонент MyComponent в качестве аргумента и возвращает новый компонент, который добавляет логирование в метод componentDidMount. Затем мы можем использовать EnhancedComponent вместо MyComponent, и при каждом отрисовке EnhancedComponent будет выводить сообщение в консоль.

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

styled components react, плюсы и минусы

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

Плюсы:

  • Большое сообщество разработчиков и пользователей, что означает, что вы можете найти множество решений и подходов к стилизации компонентов.
  • Возможность использования переменных, миксинов, функций и других функций Sass, что делает ваш код более модульным и легко поддерживаемым.
  • Возможность использования CSS-модулей, что позволяет легко организовывать ваш код и упрощать его поддержку.
  • Возможность использования инструментов для автоматической генерации стилей, таких как PostCSS или Stylus, что упрощает процесс разработки и поддержки.

Минусы:

  • Некоторые инструменты могут иметь большую степень адаптации и требовать большего времени на настройку и использование.
  • Некоторые инструменты могут иметь большую степень зависимости от конкретной конфигурации проекта, что может быть проблемой при переносе проекта на другую платформу или в другое приложение.
  • Некоторые инструменты могут иметь большую степень сложности и требовать большого опыта в разработке компонентов.
  • В целом, выбор инструмента для стилизации компонентов React зависит от ваших потребностей и предпочтений, а также от конкретных требований вашего проекта.

React Portal

React Portal - это механизм, предоставляемый библиотекой React, который позволяет разработчикам рендерить дочерние компоненты вне их родительского компонента и вставлять их в другое место в DOM-дереве. Это позволяет создавать компоненты, которые визуально находятся в одном месте в иерархии компонентов, но фактически рендерятся в другом месте в DOM.

Использование React Portal полезно в следующих случаях:

  • Модальные окна и всплывающие сообщения: Порталы позволяют разработчикам создавать модальные окна или всплывающие сообщения и вставлять их непосредственно в < body> или другие контейнеры, обеспечивая независимость от иерархии компонентов и предотвращая возможные проблемы с позиционированием и слоем наложения.
  • Интеграция с внешними библиотеками: Если вы используете сторонние библиотеки или скрипты, которые ожидают определенную DOM-структуру, вы можете использовать порталы для вставки компонентов в нужное место в DOM, не нарушая целостность остальной части приложения.
  • Управление порядком слоев: Порталы позволяют контролировать порядок слоев компонентов и управлять их взаимодействием с другими элементами на странице.
  • Пример использования React Portal:
import React from 'react';
import ReactDOM from 'react-dom';

const Modal = ({ children }) => {
  return ReactDOM.createPortal(
    <div className="modal">
      {children}
    </div>,
    document.getElementById('modal-root')
  );
};

const App = () => {
  return (
    <div>
      <h1>My App</h1>
      <Modal>
        <p>This is a modal window</p>
      </Modal>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

В приведенном примере < Modal> компонент рендерится внутри

<div id="modal-root">

вне иерархии компонентов < App>.
Это позволяет модальному окну отображаться над остальным содержимым страницы, независимо от иерархии компонентов.

Как организуются компоненты?

Организуются следующим образом:

  1. Создайте новый файл с расширением.js или.jsx в папке компонентов вашего проекта.
  2. В этом файле определите функцию-компонент, которая будет возвращать JSX-разметку.
  3. Импортируйте этот компонент в другой файл, где вы хотите использовать его.
  4. Используйте этот компонент в JSX-разметке, как обычный HTML-элемент.

Например, если вы хотите создать компонент для кнопки, вы можете создать файл Button.js со следующим содержимым:

React from 'react';

function Button(props) {
  return (
    <button onClick={props.onClick}>
      {props.label}
    </button>
  );
}

export default Button;

Затем вы можете импортировать этот компонент в другой файл и использовать его в JSX-разметке:

React from 'react';
import Button from './Button';

function App() {
  function handleClick() {
    console.log('Button clicked!');
  }

  return (
    <div>
      <Button label="Click me" onClick={handleClick} />
    </div>
  );
}

export default App;

В этом примере компонент Button принимает два свойства: label (строка) и onClick (функция). Он возвращает кнопку с этими свойствами и обработчиком события onClick. В файле App.js мы используем этот компонент, передавая ему свойства и обработчик события.

Расскажите про процесс reconciliation в React

Reconciliation в React - это процесс, который позволяет React обновлять и отображать только те компоненты, которые изменились, вместо обновления всего дерева компонентов. Это позволяет ускорить работу приложения и сделать его более эффективным.

Процесс reconciliation в React включает в себя следующие шаги:

  • React создает новое дерево компонентов, которое соответствует текущему состоянию приложения.
  • React сравнивает новое дерево компонентов с предыдущим деревом компонентов.
  • React определяет, какие компоненты были добавлены, изменены или удалены между новым и предыдущим деревом компонентов.
  • React обновляет только те компоненты, которые были изменены, добавлены или удалены.
  • React обновляет DOM только для тех компонентов, которые были изменены.
  • React вызывает жизненный цикл методов жизненного цикла (например, componentDidMount, componentDidUpdate и т.д.) только для тех компонентов, которые были изменены.

Таким образом, процесс reconciliation в React позволяет обновлять и отображать только те компоненты, которые изменились, что ускоряет работу приложения и сделает его более эффективным.

Компонент React Offscreen – зачем он нужен?

Компонент Offscreen в React не является встроенным компонентом или частью стандартной библиотеки React. Рассмотрим, зачем может быть полезным использование подобного рендеринга.

Offscreen Rendering (Рендеринг за пределами видимой области):

"Offscreen rendering" - это техника, которая позволяет выполнять рендеринг содержимого вне видимой области экрана. Это может быть полезно в нескольких сценариях:

  1. Предварительная загрузка контента: Вы можете загрузить и подготовить контент на невидимой части страницы, чтобы он был готов к отображению, когда пользователь будет готов к его просмотру. Это особенно полезно для веб-приложений с большим объемом данных, таких как виртуальные списки или сложные графики.
  2. Повышение производительности: Рендеринг компонентов за пределами видимой области может снизить нагрузку на графический процессор и CPU. Это особенно актуально для сложных анимаций или графики, которая может вызвать задержки при рендеринге.
  3. Использование вместе с техниками анимации: Если у вас есть анимированный контент, который должен появляться или исчезать на странице, предварительный рендеринг за пределами видимой области может улучшить плавность анимаций.
  4. Улучшение взаимодействия с пользователем: Рендеринг контента, который будет показан пользователю в будущем, позволяет создавать более отзывчивые интерфейсы. Например, загрузка следующей страницы в списке может быть выполнена заранее.

В React вы можете достичь "offscreen rendering" с помощью динамической загрузки компонентов, ленивой загрузки данных и рендеринга внутри невидимых контейнеров. На практике использование данной техники может зависеть от конкретных требований и сценариев вашего приложения.

Что заставляет сделать ререндер компонента?

Ререндер компонента в React происходит, когда происходят изменения в состоянии (state) или пропсах (props) компонента. React автоматически определяет, когда компонент должен быть перерисован, чтобы отразить изменения данных на пользовательском интерфейсе. Вот несколько основных факторов, которые могут вызвать ререндер компонента:

  1. Изменение состояния (state): Если компонент использует внутреннее состояние с помощью хука useState (в функциональных компонентах) или методов this.setState (в классовых компонентах), изменение состояния вызовет перерисовку компонента.
  2. Изменение пропсов (props): Когда компонент получает новые пропсы, он может быть перерисован для отображения этих новых данных.
  3. Изменение контекста (context): Если компонент использует контекст (Context API), изменение данных в контексте может привести к перерисовке компонента и всех его потомков, которые зависят от этого контекста.
  4. Изменение зависимостей в хуке useEffect: В функциональных компонентах, хук useEffect позволяет выполнить побочные эффекты, такие как запросы к серверу, при изменении определенных зависимостей. Если зависимости изменяются, компонент будет перерисован и useEffect будет вызван снова.
  5. Изменение зависимостей в хуке useMemo или useCallback: Эти хуки позволяют оптимизировать вычисления или обработку данных, но компонент будет перерисован, если зависимости этих хуков изменятся.
  6. Изменение пропсов или состояния родительского компонента: Если родительский компонент изменяет свои пропсы или состояние и передает их в дочерний компонент, дочерний компонент также будет перерисован.
  7. Изменение времени: В некоторых случаях ререндер может быть вызван таймерами, анимациями или другими асинхронными операциями.
  8. Изменение Redux-стейта или других состояний управляемых вне компонента: Если компонент использует глобальное состояние, например, с помощью библиотеки Redux, изменение этого состояния также может вызвать ререндер компонента.

Важно понимать, что React оптимизирует ререндеры и старается минимизировать количество перерисовок для улучшения производительности. Это достигается с помощью механизмов, таких как виртуальный DOM и сравнение изменений между старым и новым состоянием и пропсами.

Что такое неконтролируемые компоненты ?

Неконтролируемые компоненты (Uncontrolled Components) - это компоненты в React, значения которых не управляются напрямую через состояние компонента. Вместо этого, значения элементов формы (таких как input, select, textarea) в неконтролируемых компонентах управляются непосредственно DOM-элементами.

Это означает, что React не хранит и не обновляет значения элементов формы в состоянии компонента. Вместо этого, вы можете получить доступ к текущим значениям элементов формы через DOM-элементы напрямую, используя ссылки (refs) или другие методы. Этот подход полезен, когда вам не нужно управлять значениями элементов формы внутри React-компонента, и вы можете полагаться на DOM для этой функциональности.

Пример неконтролируемого компонента с использованием input:

class UncontrolledComponent extends React.Component {
  inputRef = React.createRef();

  handleSubmit = event => {
    event.preventDefault();
    console.log("Submitted value:", this.inputRef.current.value);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="text" ref={this.inputRef} />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

В этом примере, значение поля ввода не хранится в состоянии компонента, а доступ к значению осуществляется напрямую через this.inputRef.current.value.

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

Где в react в функциональном компоненте ты бы сделал обращение к API?

В функциональных компонентах в React лучшим местом для обращения к API является хук useEffect. useEffect позволяет выполнять побочные эффекты, такие как запросы к API, после того как компонент отрендерился на экране. Это обычно делается внутри функции-колбэка, переданной в useEffect.

Вот пример, как это может выглядеть:

import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState([]);

  useEffect(() => {
    // Выполнение запроса к API
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data))
      .catch(error => console.error('Error fetching data:', error));
  }, []); // Пустой массив зависимостей - выполнится только после первого рендера

  return (
    <div>
      {data.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

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

Кроме useEffect, вы также можете использовать другие хуки, такие как useSWR, axios, или библиотеки для управления состоянием, такие как Redux, для более сложных сценариев работы с API.

СОСТОЯНИЕ

Для чего используется Key

key - это специальный атрибут, который используется в React при рендеринге списков компонентов, чтобы помочь React оптимизировать процесс обновления и перерисовки элементов списка.

Когда вы отображаете массив элементов (например, через метод map) внутри компонента, React использует значения key для отслеживания, какие элементы были добавлены, изменены или удалены. Ключи помогают React сравнивать элементы списка между рендерами и эффективно обновлять только те элементы, которые изменились.

Важные моменты:

  1. Уникальность: Ключи должны быть уникальными в пределах списка. Каждый ключ должен отличаться от других ключей в этом списке, чтобы React мог корректно сопоставить элементы между рендерами.
  2. Стабильность: Ключи не должны меняться между рендерами, иначе React может считать элемент новым при каждом рендере, что может привести к неэффективности.

Пример использования key:

function ListItem({ item }) {
  return <li key={item.id}>{item.name}</li>;
}

function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <ListItem key={item.id} item={item} />
      ))}
    </ul>
  );
}

В этом примере, каждый элемент списка получает уникальный ключ item.id. Если элементы в массиве изменятся или будут пересортированы, React сможет эффективно обновить только изменившиеся элементы в DOM.

Без использования ключей, React может столкнуться с проблемами при обновлении списка, что может привести к непредсказуемому поведению и плохой производительности.

Для чего нужны state и props в React?

В React state и props используются для управления и передачи данных между компонентами.

  • state: state представляет собой объект, содержащий данные, которые могут изменяться внутри компонента. Когда state изменяется, React перерисовывает компонент и обновляет соответствующие элементы интерфейса. state является локальным для компонента и управляется самим компонентом. Он может быть инициализирован в конструкторе компонента и изменен с помощью метода setState(). Пример использования state:
import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  incrementCount() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={() => this.incrementCount()}>Increment</button>
      </div>
    );
  }
}

В этом примере компонент Counter имеет внутреннее состояние count, которое увеличивается при нажатии кнопки "Increment". При изменении состояния с помощью setState(), React обновляет компонент и обновляет отображение счетчика на странице.

  • props: props (сокращение от "properties") представляют собой значения, передаваемые компоненту из его родительского компонента. props являются неизменяемыми и служат для передачи данных и настроек в компоненты. Родительский компонент передает props дочернему компоненту, и дочерний компонент может использовать эти значения в своей реализации. Пример использования props:
import React from 'react';

function Greeting(props) {
  return <p>Hello, {props.name}!</p>;
}

function App() {
  return <Greeting name="John" />;
}

В этом примере компонент App передает name со значением "John" компоненту Greeting через props. Компонент Greeting отображает приветствие с использованием переданного имени.

Использование state и props позволяет компонентам React быть динамическими и взаимодействовать друг с другом, передавая и обновляя данные. state используется для управления локальными данными внутри компонента, в то время как props используются для передачи данных от родительского компонента к дочерним компонентам.

Расскажи про контекст в React

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

Контекст особенно полезен, когда несколько компонентов в иерархии нуждаются в доступе к одним и тем же данным. Вместо того чтобы передавать эти данные через несколько уровней компонентов с помощью пропсов, контекст позволяет установить "поставщика" контекста на верхнем уровне иерархии, а затем "потребителям" контекста получать данные из этого контекста в любом месте вниз по иерархии.

В React контекст реализуется с помощью двух основных компонентов: Provider (поставщик) и Consumer (потребитель).

  • Provider: Компонент, который устанавливает контекст и предоставляет данные для него. Оборачивая родительские компоненты в Provider, мы делаем контекст и его данные доступными для всех дочерних компонентов в иерархии.
  • Consumer: Компонент, который использует контекст и получает данные из него. Оборачивая компонент в Consumer, мы можем получить доступ к данным контекста и использовать их внутри компонента.

Пример использования контекста в React:

// Создаем контекст
const MyContext = React.createContext();

// Компонент-поставщик
class MyProvider extends React.Component {
  state = {
    data: 'Some data'
  };

  render() {
    return (
      <MyContext.Provider value={this.state.data}>
        {this.props.children}
      </MyContext.Provider>
    );
  }
}

// Компонент-потребитель
class MyConsumer extends React.Component {
  render() {
    return (
      <MyContext.Consumer>
        {data => <div>Data from context: {data}</div>}
      </MyContext.Consumer>
    );
  }
}

// Использование компонентов
class App extends React.Component {
  render() {
    return (
      <MyProvider>
        <div>
          <h1>My App</h1>
          <MyConsumer />
        </div>
      </MyProvider>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

В этом примере мы создаем контекст MyContext с помощью React.createContext(). Затем у нас есть компонент-поставщик MyProvider, который устанавливает значение контекста (state.data) с помощью <MyContext.Provider>. Вложенный компонент MyConsumer оборачивается в <MyContext.Consumer>, чтобы получить значение контекста и использовать его внутри компонента.

В компоненте App мы оборачиваем иерархию компонентов в , чтобы сделать контекст и данные доступными для компонентов ниже по иерархии. В компоненте MyConsumer мы выводим данные из контекста внутри < div>.

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

Ref и key, что это?

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

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

Пример:

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>
    {number}
  </li>
);

Если у вас нет постоянных идентификаторов для отрисовываемых элементов, в крайнем случае вы можете использовать индекс элемента в качестве ключа

Если явный ключ для списка элементов не назначен, тогда React по умолчанию будет использовать индексы в качестве ключей. Не рекомендуется использовать индексы для ключей, если порядок элементов может измениться.

Ключи имеют смысл только в контексте окружающего массива. Например, если вы выделяете компонент ListItem, вам нужно присваивать ключ элементам <ListItem /> в массиве, а не элементам <li> в самом ListItem.

Пример:

function ListItem(props) {
  const value = props.value;
  return (
    // Неправильно! Здесь не нужно указывать ключ:
    <li key={value.toString()}>
      {value}
    </li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // Неправильно! Здесь должен быть указан ключ:
    <ListItem value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

Refs (ссылки) предоставляют способ доступа к DOM-узлам или React-элементам, созданным в методе render().

Есть несколько хороших примеров использования ссылок:

  • Управление фокусом, выделение текста или воспроизведение медиаресурсами.
  • Выполнение анимаций в императивном подходе.
  • Интеграция со сторонними библиотеками, взаимодействующие с DOM.

Ссылки создаются с использованием React.createRef() и добавляются к React-элементам с помощью атрибута ref. Ссылки обычно присваиваются свойству экземпляра, когда компонент создаётся таким образом, чтобы на них можно было ссылаться по всему компоненту.

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

Когда ссылка передаётся элементу в render(), доступ к её узлу можно получить в атрибуте current.

  • Когда атрибут ref используется в HTML-элементе, ref, созданный в конструкторе с помощью React.createRef(), получает базовый элемент DOM в качестве своего свойства current.
  • Когда атрибут ref используется на пользовательском классовом компоненте, объектref получает примонтированный экземпляр компонента в качестве своего свойства current.
  • Вы не можете использовать атрибут ref в функциональных компонентах, потому что у них не может быть экземпляров. Однако вы можете использовать атрибут ref внутри функционального компонента, если вы ссылаетесь на DOM-элемент или классовый компонент.

Почему не использовать встроенный контекст?

Встроенный контекст в React представлен API, который позволяет передавать данные через компонентное дерево без явной передачи пропсов между каждым компонентом. Однако, хотя встроенный контекст может быть полезным в некоторых сценариях, есть несколько причин, почему его использование может быть ограничено или не рекомендуется:

  1. Сложность чтения и отладки: Использование контекста может сделать код менее читаемым и сложным для понимания, так как данные передаются неявно через компонентное дерево. Это может усложнить отладку, особенно при комплексных компонентных структурах.
  2. Затруднение в тестировании: При использовании контекста, необходимо обеспечивать наличие правильных значений контекста при тестировании компонентов. Это может усложнить тестирование и создание изолированных тестовых сценариев.
  3. Сценарии ограниченного применения: Встроенный контекст не является идеальным для всех сценариев. Он хорошо подходит для передачи данных, которые действительно являются "глобальными" для приложения. В случае, если вам нужно передавать данные между компонентами на более низком уровне и эти данные не считаются глобальными, использование пропсов или других управляемых средств передачи данных может быть предпочтительнее.
  4. Поддержка и обратная совместимость: API встроенного контекста может меняться в будущих версиях React. Это может привести к проблемам с обратной совместимостью и требовать пересмотра кода при обновлении.

Вместо использования встроенного контекста, иногда рекомендуется использовать управляемый пропсами способ передачи данных или более специфичные для сценария решения, такие как Redux для управления состоянием или библиотеки для управления темами/стилями. Но как и с любой технологией, выбор зависит от конкретных требований и архитектуры вашего приложения.

Как передать данные из дочернего в родительский

Для передачи данных из дочернего компонента в родительский компонент в React можно использовать пропсы (props).

  • Пропсы - это свойства, которые передаются из родительского компонента в дочерний компонент. Дочерний компонент может использовать эти пропсы для отображения данных или выполнения действий.

Например, если у вас есть родительский компонент ParentComponent и дочерний компонент ChildComponent , и вы хотите передать данные из ChildComponent в ParentComponent , вы можете сделать следующее:

В ChildComponent определите функцию, которая будет вызываться при изменении данных. Например:

function handleDataChange(data) {
  // Обновите состояние компонента
  this.setState({ data });
  // Отправьте событие на родительский компонент
  this.props.onDataChange(data);
}

В ChildComponent передайте функцию handleDataChange в качестве пропса onDataChange :

<ChildComponent onDataChange={handleDataChange} />

В ParentComponent определите функцию, которая будет вызываться при изменении данных в ChildComponent . Например:

function handleDataChange(data) {
  // Обновите состояние компонента ParentComponent
  this.setState({ childData: data });
}

В ParentComponent передайте функцию handleDataChange в качестве обработчика события onDataChange из ChildComponent :

<ChildComponent onDataChange={handleDataChange.bind(this)} />

Теперь при изменении данных в ChildComponent функция handleDataChange будет вызываться, которая отправит событие на ParentComponent с обновленными данными. В ParentComponent вы можете определить функцию handleDataChange , которая будет обновлять состояние компонента ParentComponent с помощью новых данных.

Что такое React.Context?

React.Context - это механизм, предоставляемый библиотекой React, для передачи данных вниз по иерархии компонентов без явной передачи пропсов через промежуточные компоненты. Он позволяет создавать глобальные переменные, доступные для чтения и записи из разных компонентов приложения.

React.Context состоит из двух основных компонентов:

  • Context Provider (поставщик контекста): Компонент, который определяет контекст и предоставляет данные. Он оборачивает дерево компонентов, которым требуется доступ к этим данным. Context Provider обычно создается с использованием createContext функции из библиотеки React.
  • Context Consumer (потребитель контекста): Компонент, который использует контекст и получает доступ к данным, предоставленным Context Provider. Он может быть размещен в любом месте дерева компонентов, вложенном в Context Provider. Context Consumer используется для чтения данных из контекста и реагирования на их изменения.

Использование React.Context позволяет избежать "прокидывания пропсов" через несколько промежуточных компонентов, особенно в случаях, когда данные нужны во множестве компонентов на разных уровнях иерархии. Он также облегчает обновление контекста и обработку изменений данных в приложении.

Пример использования React.Context:

// Создание контекста
const MyContext = React.createContext();

// Компонент-поставщик контекста
const MyContextProvider = ({ children }) => {
  const data = "Здесь находятся данные контекста";

  return (
    <MyContext.Provider value={data}>
      {children}
    </MyContext.Provider>
  );
};

// Компонент-потребитель контекста
const MyComponent = () => {
  return (
    <MyContext.Consumer>
      {(data) => <p>{data}</p>}
    </MyContext.Consumer>
  );
};

// Использование контекста
const App = () => {
  return (
    <MyContextProvider>
      <MyComponent />
    </MyContextProvider>
  );
};

В этом примере MyContextProvider определяет контекст и предоставляет данные "Здесь находятся данные контекста". Компонент MyComponent читает эти данные из контекста и отображает их. App является верхнеуровневым компонентом, содержащим MyContextProvider и MyComponent, и обеспечивает доступ к контексту для MyComponent.

Что такое Prop drilling

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

Когда вы хотите изменить состояние компонента, вы передаете его в качестве пропса в родительский компонент, а затем этот пропс передается дальше по цепочке компонентов до тех пор, пока не будет достигнут компонент, который может изменить его состояние. Этот процесс называется "prop drilling".

Например, если у вас есть компонент "ChildComponent", который должен изменить состояние компонента "ParentComponent", вы можете передать функцию обратного вызова в качестве пропса из "ChildComponent" в "ParentComponent". Затем вы можете вызвать эту функцию обратного вызова в "ChildComponent", чтобы изменить состояние "ParentComponent".

function ParentComponent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <ChildComponent setCount={setCount} />
    </div>
  );
}

function ChildComponent(props) {
  const handleClick = () => {
    props.setCount(count + 1);
  };

  return (
    <div>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

В этом примере мы передаем функцию обратного вызова "setCount" в качестве пропса из "ChildComponent" в "ParentComponent". Затем мы вызываем эту функцию обратного вызова в "ChildComponent", чтобы изменить состояние "ParentComponent".

Что будет если мы мутируем стейт, вместо того, чтобы выдавать новый объект?

Мутация стейта (state mutation) может привести к нежелательным побочным эффектам, таким как неожиданное изменение других частей приложения или ошибки в работе приложения. Кроме того, мутации могут быть трудными для отладки и тестирования, так как они могут быть сложными для восстановления после ошибок.

Вместо того, чтобы мутировать стейт, лучше использовать оптимистическое обновление (optimistic update), которое позволяет клиенту предсказывать, какие изменения ожидаются в стейте, прежде чем они внесены на сервер. Это позволяет уменьшить количество ошибок и упростить отладку.

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

Является ли верным такое использование свойства (prop) key:

interface IItem {
name: string;
};
const MyList: FC = (props: { items: IItem[] }) => {
return (
<ul>
{props.items.map((item: IItem) => (
<li key={item.name}>{item.name}</li>
))}
</ul>
);
}

Да, это верное использование свойства key в компоненте React.

Свойство key используется для идентификации элементов в списке и для оптимизации производительности React. Когда компонент перерисовывается, React использует свойство key для определения, какие элементы должны быть добавлены, изменены или удалены.

В данном случае, свойство key используется для идентификации элементов списка по имени. Каждый элемент списка имеет уникальное имя, которое используется в свойстве key. Это позволяет React понимать, какие элементы нужно изменить или удалить при обновлении компонента, и оптимизировать производительность.

Как взаимодействовать с элементом через React, если требуется обратиться к нему?

В React для взаимодействия с элементами используется подход, основанный на создании ссылок (refs). Чтобы обратиться к элементу и работать с ним, можно использовать ref для его определения.

Пример:

import React, { useRef } from 'react';

function MyComponent() {
  // Создаем ref
  const myElementRef = useRef(null);

  // Функция, которая будет вызвана после монтирования компонента
  const handleMount = () => {
    // Здесь можно обращаться к элементу через ref
    if (myElementRef.current) {
      // Доступ к DOM-элементу происходит через .current
      myElementRef.current.style.backgroundColor = 'red';
    }
  };

  return (
    <div>
      {/* Привязываем ref к элементу */}
      <div ref={myElementRef}>Это элемент, с которым нужно взаимодействовать</div>
      <button onClick={handleMount}>Нажать для обращения к элементу</button>
    </div>
  );
}

В этом примере мы создаем ref с помощью useRef, затем привязываем его к DOM-элементу <div> с помощью ref={myElementRef}. Когда пользователь нажимает на кнопку, вызывается функция handleMount, в которой мы обращаемся к элементу через myElementRef.current и, например, меняем его стиль. Это позволяет нам взаимодействовать с элементами напрямую в React-приложениях.

ОПТИМИЗАЦИЯ

Оптимизации встроенными инструментами из React

Мемоизация:

  1. React.memo это HOC, поверхностно сравнивает компоненты между отрисовками и если входные параметры (props) не изменились, то не вызывает рендер компонента, то есть мемоизирует компонент.
  2. useMemo возвращает мемоизированное значение.
  3. useCallback возвращает мемоизированный колбэк.

Удаление встроенных функций — это функции, которые определяются и передаются при рендеринге компонентов. Вот пример плохой оптимизации:

import Child from 'components/Child'

const Parent = () => (
 <Child onClick={() => {
   console.log('Случился клик!')
 }} />
)

export default Parent

В нашем коде имеется встроенная функция. С такими функциями сопряжено 2 главных проблемы:

  1. Они запускают повторный рендеринг компонента даже в случае, когда пропы остались прежними.
  2. Это, в свою очередь, увеличивает расход памяти.

Решение: выносим встроенные функции из рендеринга компонента.

import Child from 'components/Child'

const Parent = () => {
 const handleClick = () => {
   console.log('Случился клик!')
 }

 return (
   <Child onClick={handleClick} />
 )
}

Условный рендеринг позволяет также оптимизировать код и улучшить производительность приложения. В примере ниже пример использования условного рендеринга в модальном окне:

import { useState } from 'react'
import { Modal, Button } from 'someCSSFramework'

const Modal = ({ isOpen, title, body, onClose }) => {
 const [open, setOpen] = useState(isOpen || false)

 const handleClick =
   typeof onClose === 'function'
     ? onClose
     : () => { setOpen(false) }

 // условный рендеринг
 if (!open) return null

 return (
   <Modal show={open}>
     <Button onClick={handleClick}>x<Button>
     <Modal.Header>{title}</Modal.Header>
     <Modal.Body>{body}</Modal.Body>
   </Modal>
 )
}

Если мы применяем LazyLoading какие изменения появляются в сборке

Применение LazyLoading может привести к изменениям в сборке, если оно не было оптимизировано для этого подхода. LazyLoading - это подход к загрузке модулей, при котором модули загружаются только при необходимости, а не сразу после загрузки всего приложения.

Если мы применяем LazyLoading, то модули могут быть загружены только при необходимости, что может привести к увеличению времени загрузки приложения. Кроме того, если мы не оптимизируем сборку для LazyLoading, то это может привести к увеличению размера сборки и ухудшению производительности приложения.

Для оптимизации сборки для LazyLoading можно использовать различные инструменты и технологии, такие как Webpack, Rollup, Parcel и т.д. Эти инструменты позволяют оптимизировать сборку, удалять неиспользуемый код, минифицировать и оптимизировать код и т.д.

Например, в Webpack можно использовать плагин webpack-bundle-analyzer , который позволяет анализировать размеры и количество модулей в сборке и оптимизировать их для LazyLoading. Кроме того, можно использовать другие плагины и инструменты, такие как tree-shaking , code-splitting и т.д., чтобы улучшить производительность и оптимизировать сборку.

Таким образом, применение LazyLoading может привести к изменениям в сборке, если оно не было оптимизировано для этого подхода. Для оптимизации сборки можно использовать различные инструменты и технологии, такие как Webpack, Rollup, Parcel и т.д.

Можно ли обернуть все переменные и функции в useMemo useCallback? В чем минусы

Да, можно обернуть все переменные и функции в useMemo и useCallback , чтобы избежать ненужных перерисовок компонента и повысить производительность.

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

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

Однако, необходимо учитывать, что использование useMemo и useCallback может привести к утечкам памяти, если не используется правильно. Например, если функция, переданная в useCallback , зависит от переменной, которая изменится при каждом рендере компонента, то useCallback будет создавать новую функцию при каждом рендере, что может привести к утечкам памяти.

Не стоит использовать useMemo и useCallback если операция не затратна по времени и не нагружает память. Например, большинство встроенных методов JavaScript оптимизированы, и нет необходимости запоминать возвращаемое ими значение при каждом вызове. Вызов хуков сам по себе создает нагрузку, и их применение может не окупиться.

Также, необходимо учитывать, что использование useMemo и useCallback может усложнить код и затруднить его понимание и поддержку. Поэтому, перед использованием useMemo и useCallback , необходимо убедиться, что они действительно необходимы и не приведут к ухудшению производительности и качества кода.

Каким образом можно достичь повторного использования общей логики между двумя компонентами в React?

В React существует несколько способов достижения повторного использования общей логики между двумя компонентами:

  1. Вынос логики в отдельный кастомный хук (Custom Hook): Вы можете создать свой собственный хук, содержащий общую логику, и затем использовать этот хук в любых компонентах, где требуется эта логика. Кастомные хуки позволяют абстрагировать логику от компонентов и обеспечивают повторное использование.Пример:// useCommonLogic.js import { useState } from 'react'; function useCommonLogic() { const [count, setCount] = useState(0); const increment = () => { setCount(count + 1); }; return { count, increment }; } export default useCommonLogic; // ComponentA.js import React from 'react'; import useCommonLogic from './useCommonLogic'; function ComponentA() { const { count, increment } = useCommonLogic(); return ( <div> <p>Component A Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); } // ComponentB.js import React from 'react'; import useCommonLogic from './useCommonLogic'; function ComponentB() { const { count, increment } = useCommonLogic(); return ( <div> <p>Component B Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); }
  2. Композиция компонентов: Создайте общий компонент, который содержит общую логику, а затем используйте его в других компонентах, чтобы получить доступ к этой логике через пропсы.Пример:// CommonComponent.js import React, { useState } from 'react'; function CommonComponent() { const [count, setCount] = useState(0); const increment = () => { setCount(count + 1); }; return ( <div> <p>Common Component Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); } export default CommonComponent; // ComponentA.js import React from 'react'; import CommonComponent from './CommonComponent'; function ComponentA() { return ( <div> <CommonComponent /> </div> ); } // ComponentB.js import React from 'react'; import CommonComponent from './CommonComponent'; function ComponentB() { return ( <div> <CommonComponent /> </div> ); }

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

Использование Higher-Order Components (HOC)

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

function withHeader(Component) {
  return function WithHeader(props) {
    return (
      <>
        <h1>Common Header</h1>
        <Component {...props} />
      </>
    );
  };
}

function ComponentA() {
  return <div>Component A</div>;
}

const ComponentWithHeaderA = withHeader(ComponentA);

function ComponentB() {
  return <div>Component B</div>;
}

const ComponentWithHeaderB = withHeader(ComponentB);

// Использование
ReactDOM.render(
  <>
    <ComponentWithHeaderA />
    <ComponentWithHeaderB />
  </>,
  document.getElementById("root")
);

Использование Render Props

Render Props - это техника, при которой компонент принимает функцию в качестве свойства (props) и использует эту функцию для возврата содержимого, которое будет отображено. Можно передавать различные функции для повторного использования общей логики. Ниже приведен пример использования Render Props для отображения текущей даты и времени:

function DateTime(props) {
  const date = new Date();
  return props.render(date);
}

function ComponentA() {
  return (
    <DateTime
      render={(date) => (
        <div>
          Component A - Current Date: {date.toLocaleString()}
        </div>
      )}
    />
  );
}

function ComponentB() {
  return (
    <DateTime
      render={(date) => (
        <div>
          Component B - Current Date: {date.toLocaleString()}
        </div>
      )}
    />
  );
}

// Использование
ReactDOM.render(
  <>
    <ComponentA />
    <ComponentB />
  </>,
  document.getElementById("root")
);

Композиция компонентов

React предоставляет мощный механизм композиции компонентов, который позволяет создавать более сложные компоненты из простых и повторно использовать их. Это достигается путем создания компонентов, которые содержат или оборачивают другие компоненты, чтобы объединить их функциональность. Такой подход позволяет создавать модульный код, где каждый компонент отвечает только за свою собственную ответственность.

Higher-Order Components (HOC)

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

Render Props

Render Props - это паттерн, который позволяет компонентам передавать функцию через свойство (props), которая будет использоваться для рендеринга содержимого. Это позволяет изолировать общую логику в отдельном компоненте и передавать различные функции в компоненты для повторного использования этой логики. В результате, компоненты становятся более гибкими и могут адаптироваться к разным потребностям и сценариям использования.

React Hooks

Введение React Hooks в React 16.8 добавило еще больше возможностей для повторного использования логики между компонентами. Хуки, такие как useEffect, useMemo, useCallback, позволяют изолировать и повторно использовать определенные фрагменты логики в функциональных компонентах. Например, useEffect позволяет выполнять определенные действия при изменении определенных зависимостей, что может быть полезно для общей логики, связанной с эффектами или подписками

Библиотеки и фреймворки для повторного использования

Существуют различные библиотеки и фреймворки, которые помогают в повторном использовании общей логики в React. Например, Redux предоставляет возможности для управления глобальным состоянием приложения, что позволяет легко использовать общую логику между различными компонентами. Библиотеки, такие как React Router, предоставляют средства для управления маршрутизацией и повторного использования компонентов, связанных с навигацией.

Как увеличить производительность?

Существует несколько способов увеличить производительность сайта:

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

Перфоменс трюки в React

В React есть множество трюков, которые могут быть полезными в различных ситуациях. Некоторые из них:

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

Что такое React.Lazy?

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

React.Lazy() принимает функцию, которая возвращает компонент, который нужно загрузить. Например:

import React, { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </div>
  );
}

export default App;

В этом примере компонент LazyComponent будет загружаться только при первом использовании в приложении. Если компонент не используется, то загрузка не произойдет.

Если вы выполняете маппинг коллекции без указания явного ключа, какой ключ будет использоваться по умолчанию? Какой ключ будет использоваться для каждого компонента?

По умолчанию в React используется ключ _id , который автоматически генерируется при добавлении нового элемента в коллекцию. Ключ _id является уникальным идентификатором элемента в коллекции.

Ключ для каждого компонента может быть определен явно, используя атрибут key . Если ключ не указан явно, React будет использовать ключ по умолчанию, который может быть неуникальным или нестабильным.

Например, если вы хотите отобразить список элементов в компоненте List , вы можете использовать ключ _id для каждого элемента:

function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item._id}>{item.name}</li>
      ))}
    </ul>
  );
}

Если вы хотите отобразить список элементов в компоненте List с использованием компонента Item , вы можете использовать ключ item._id для каждого элемента:

function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <Item key={item._id} item={item} />
      ))}
    </ul>
  );
}

function Item({ item }) {
  return (
    <li>{item.name}</li>
  );
}

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

Зачем нужна функция lazy в React?

Функция lazy в React используется для ленивой загрузки компонентов. Ленивая загрузка позволяет отложить загрузку компонентов до тех пор, пока они не понадобятся для отображения на странице. Это особенно полезно для компонентов, которые редко используются или требуют больших ресурсов.

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

Преимущества использования lazy:

  1. Улучшение производительности: Ленивая загрузка позволяет снизить начальное время загрузки приложения, поскольку компоненты, которые не нужны на начальном этапе, не будут загружены сразу.
  2. Оптимизация ресурсов: Компоненты, которые редко используются или требуют больших ресурсов, не будут загружаться, пока не станут необходимы.

Пример использования lazy:

import React, { lazy, Suspense } from 'react';

// Импортируем компонент с помощью lazy
const MyLazyComponent = lazy(() => import('./MyLazyComponent'));

function App() {
  return (
    <div>
      {/* Оборачиваем компонент в Suspense, чтобы обработать пока компонент загружается */}
      <Suspense fallback={<div>Loading...</div>}>
        <MyLazyComponent />
      </Suspense>
    </div>
  );
}

Здесь MyLazyComponent будет загружаться только при монтировании компонента App и его отображении на странице. Пока компонент загружается, будет показан компонент загрузки (fallback), определенный в Suspense.

ОБЩЕЕ

Что такое Shadow DOM

Shadow DOM

Теневой DOM («Shadow DOM») используется для инкапсуляции. Благодаря ему в компоненте есть собственное «теневое» DOM-дерево, к которому нельзя просто так обратиться из главного документа, у него могут быть изолированные CSS-правила и т.д.

Каждый DOM-элемент может иметь 2 типа поддеревьев DOM:

Light tree – обычное, «светлое», DOM-поддерево, состоящее из HTML-потомков. Все поддеревья, о которых мы говорили в предыдущих главах, были «light».

Shadow tree – скрытое, «теневое», DOM-поддерево, не отражённое в HTML, скрытое от посторонних глаз.

Если у элемента имеются оба поддерева, браузер отрисовывает только теневое дерево. Также мы всё же можем задать «композицию» теневого и обычного деревьев.

Теневое дерево можно использовать в пользовательских элементах (Custom Elements), чтобы спрятать внутренности компонента и применить к ним локальные стили.

Например, этот

<script>
customElements.define('show-hello', class extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({mode: 'open'});
    shadow.innerHTML = `<p>
      Hello, ${this.getAttribute('name')}
    </p>`;
  }
});
</script>

<show-hello name="John"></show-hello>

В чем разница между классовыми и функциональными компонентами

Разница между классовыми и функциональными компонентами заключается в способе определения и использования компонентов в React.

  1. Синтаксис и объявление:

Классовые компоненты: Определяются как классы, расширяющие React.Component. Имеют конструктор и методы жизненного цикла (например, componentDidMount, render и т.д.).

Функциональные компоненты: Определяются как обычные функции. Вводятся с помощью функции, принимающей пропсы и возвращающей JSX (или другие элементы).

  1. Структура кода:

Классовые компоненты: Обычно требуют больше кода из-за объявления класса, наличия конструктора и необходимости биндинга методов в случае использования обработчиков событий.

Функциональные компоненты: Требуют меньше кода, так как они просто функции. Введение хуков (hooks) дало им способность управлять состоянием и использовать другие функциональные возможности, которые ранее были доступны только классовым компонентам.

  1. Состояние и жизненный цикл:

Классовые компоненты: Имеют полный доступ к состоянию и жизненному циклу. Это было классическим способом управления состоянием компонентов до введения хуков.

Функциональные компоненты: Сначала функциональные компоненты были более ограниченными, но с введением хуков (например, useState, useEffect и т.д.), они теперь могут управлять состоянием и жизненным циклом аналогично классовым компонентам.

  1. Производительность:

Классовые компоненты: Исторически они могли вызывать некоторые проблемы с производительностью из-за необходимости связывать обработчики событий и жизненные циклы.

Функциональные компоненты: С хуками и введением оптимизаций, таких как React.memo, функциональные компоненты обычно имеют лучшую производительность.

  1. Требования к версии:

Классовые компоненты: Могут использоваться в любой версии React.

Функциональные компоненты: Требуют версию React 16.8 и выше для использования хуков.

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

Классовые компоненты

Раньше, когда React был в версии до 16.8, классовые компоненты были основным способом создания компонентов. Они имеют встроенное состояние (state) и поддерживают методы жизненного цикла. Вот пример классового компонента:

import React, { Component } from 'react';

class ClassComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={() => this.handleClick()}>Increment</button>
      </div>
    );
  }
}

Функциональные компоненты

Введение хуков (hooks) в React 16.8 позволило использовать функциональные компоненты как более простой и предпочтительный способ создания компонентов. Они не имеют встроенного состояния и используют хуки, такие как useState, useEffect и другие, для управления состоянием и выполнения побочных эффектов. Вот пример функционального компонента:

import React, { useState } from 'react';

function FunctionalComponent() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

Функциональные компоненты

  • Хуки (hooks), введенные в React 16.8, позволили функциональным компонентам иметь состояние и выполнять побочные эффекты без необходимости создания класса. Это сделало функциональные компоненты более простыми и понятными для разработчиков.
  • Хуки, такие как useState, useEffect, useContext и другие, предоставляют более гибкий и мощный способ управления состоянием и выполнения действий в функциональных компонентах.
  • Функциональные компоненты обычно имеют меньший объем кода и легче поддерживаются. Они позволяют разделить компоненты на более мелкие и переиспользуемые блоки, что способствует повторному использованию кода и облегчает его тестирование.
  • Функциональные компоненты работают быстрее, чем классовые компоненты, благодаря отсутствию накладных расходов на создание экземпляра класса.

Классовые компоненты

  • Классовые компоненты были первоначальным способом создания компонентов в React. Они имеют встроенное состояние (state) и методы жизненного цикла, такие как componentDidMount, componentDidUpdate, componentWillUnmount и другие.
  • Методы жизненного цикла классовых компонентов предоставляют возможность выполнения определенных действий на различных этапах жизненного цикла компонента, например, инициализация состояния, загрузка данных или очистка ресурсов перед удалением компонента.
  • Классовые компоненты могут быть полезны, если вам требуется управлять сложным состоянием, которое требует более продвинутых операций, или если вы работаете с кодом, который использует старые версии React и не поддерживает функциональные компоненты.

Что такое VirtualDOM

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

Использование виртуального DOM позволяет React работать более эффективно и быстро обновлять пользовательский интерфейс. Вместо того, чтобы каждый раз перерисовывать всю страницу при изменении данных, React анализирует только изменения и обновляет только те части страницы, которые действительно нуждаются в обновлении. Это позволяет сделать взаимодействие с веб-приложением более плавным и отзывчивым для пользователей.

В чем разница между виртуальным DOM и обычным?

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

Вот как это работает:

Реальный DOM: Веб-страницы строятся на основе дерева элементов HTML, которое называется Реальным DOM. Каждый раз, когда данные в приложении меняются, Реальный DOM должен быть обновлен, что может привести к затратам по производительности, так как обновление DOM - операция ресурсоемкая.

Виртуальный DOM: Виртуальный DOM - это виртуальное представление Реального DOM, созданное на стороне JavaScript. Каждый компонент в React имеет свое представление в Виртуальном DOM. Это представление является легким и быстрым для обновления.

Обновление: Когда состояние компонента меняется, React сначала обновляет Виртуальный DOM. Затем происходит процесс сравнения предыдущего и нового состояний Виртуального DOM. Этот процесс называется "сверка" (reconciliation).

Diffing: React сравнивает старое и новое состояния Виртуального DOM, чтобы выяснить, какие части интерфейса нужно обновить. Это делается путем сравнения структуры и значений узлов.

Минимизация мутаций: React определяет минимальное количество изменений, которое необходимо внести в Реальный DOM, чтобы обновить его до нового состояния Виртуального DOM. Это уменьшает накладные расходы, связанные с манипуляциями с DOM.

Пакетная обработка: React собирает все изменения, которые должны быть применены к Реальному DOM, и применяет их пакетно. Это позволяет избежать лишних перерисовок и оптимизировать процесс обновления интерфейса.

Используя Виртуальный DOM, React сокращает нагрузку на браузер, улучшает производительность и предоставляет более удобные средства для работы с интерфейсом.

Что такое React? Зачем он нужен? Какие задачи решает?

React - это библиотека JavaScript для создания пользовательских интерфейсов. Она позволяет разработчикам создавать компоненты, которые могут быть повторно использованы в разных частях приложения. React решает несколько задач, таких как:

  • Управление состоянием: React позволяет управлять состоянием приложения с помощью компонентов. Каждый компонент имеет свое состояние, которое может быть изменено только через определенные методы.
  • Рендеринг: React использует дерево компонентов для отображения интерфейса. Каждый компонент рендерит свой собственный HTML-код, который затем отображается на странице.
  • Обновление: React обновляет только те части интерфейса, которые изменились. Это позволяет ускорить работу приложения и сделать его более эффективным.
  • Модульность: React позволяет разбивать приложение на модули, которые могут быть повторно использованы в разных частях. Это упрощает разработку и поддержку приложения.
  • Гибкость: React позволяет разработчикам создавать гибкие и масштабируемые приложения. Он предоставляет множество инструментов для управления состоянием, рендерингом и обновлением.
React является одной из самых популярных библиотек для создания пользовательских интерфейсов веб-приложений. Она широко используется во многих компаниях и проектах, и имеет большое сообщество разработчиков, которые создают множество дополнительных инструментов и библиотек для упрощения разработки.

Реакт быстрый из-за того, что он сравнивает реальный дом и виртуал дом. Можешь рассказать как это происходит?

Когда вы используете React, виртуальный DOM используется для ускорения процесса обновления пользовательского интерфейса. Виртуальный DOM - это копия реального DOM, которая хранится в памяти и обновляется только при изменении состояния компонентов.

Когда вы изменяете состояние компонента, React обновляет виртуальный DOM, а затем сравнивает его с предыдущим виртуальным DOM. Если изменения были внесены, React обновляет реальный DOM, чтобы отобразить изменения.

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

Таким образом, если вы хотите обновлять состояние компонента слишком часто, вам может потребоваться использовать другие методы, такие как setTimeout или requestAnimationFrame, чтобы уменьшить количество обновлений.

В чем разница между NextJS и ReactJS

Next.js и ReactJS - это две разные библиотеки для создания пользовательских интерфейсов на языке JavaScript.

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

ReactJS - это библиотека для создания пользовательских интерфейсов на языке JavaScript. Она предоставляет множество инструментов и функций для создания компонентов, управления состоянием и обработки событий. ReactJS также поддерживает использование TypeScript и других языков программирования на стороне клиента.

Основная разница между Next.js и ReactJS заключается в том, что Next.js является фреймворком, который упрощает разработку веб-приложений, в то время как ReactJS является библиотекой, которая предоставляет инструменты для создания пользовательских интерфейсов. Next.js предоставляет множество инструментов и функций, которые упрощают разработку, в то время как ReactJS предоставляет только базовые инструменты для создания компонентов и управления состоянием.

В чём отличие ReactDOM.render от ReactDOM.hydrate (или createRoot от hydrateRoot )?

ReactDOM.render и ReactDOM.hydrate являются двумя разными методами в библиотеке React для рендеринга компонентов. Они имеют следующие отличия:

  1. Используемое начальное состояние:
    • ReactDOM.render: Этот метод используется для первоначального рендеринга компонентов на стороне клиента (client-side rendering) или на сервере (server-side rendering). Он не предполагает наличие предыдущего состояния на стороне клиента и начинает рендеринг с нуля.
    • ReactDOM.hydrate: Этот метод также используется для рендеринга компонентов, но предполагает наличие предыдущего HTML-содержимого на стороне клиента. Он применяется, когда React пытается "гидратировать" (hydrate) существующее HTML-содержимое и связать его с соответствующими компонентами. Это важно для оптимизации производительности, так как React пытается сохранить согласованность между виртуальным DOM и реальным DOM, включая обработку событий и сохранение состояния.
  2. Применение:
    • ReactDOM.render: Используется для рендеринга на стороне клиента в том случае, когда у вас нет предыдущего HTML-содержимого для "гидратации".
    • ReactDOM.hydrate: Используется для рендеринга на стороне клиента, когда у вас уже есть предыдущее HTML-содержимое, которое было сгенерировано на сервере или на стороне клиента и затем отправлено на сервер и получено обратно (по обычному процессу серверного рендеринга или через такие инструменты, как Next.js). Этот метод помогает React сохранить согласованность существующего состояния на стороне клиента.

Что касается createRoot и hydrateRoot, они предоставляют альтернативный способ для рендеринга компонентов, вводимый в React 18. Эти методы используются в новой модели совместного рендеринга (Concurrent Mode) и заменяют старый метод ReactDOM.render. Они обеспечивают более гибкий и предсказуемый способ рендеринга компонентов, а также позволяют React оптимально управлять приоритетами и работать в режиме "совместной работы" для более высокой производительности. hydrateRoot в основном используется для гидратации уже существующего контента, а createRoot - для создания нового корневого узла для рендеринга.

Расскажи процесс рендеринга в React

Процесс рендеринга в React включает в себя следующие шаги:

  • Создание компонента: Сначала необходимо создать компонент в React. Компонент может быть создан с помощью функционального или классового компонента.
  • Создание элемента: Затем необходимо создать элемент, который будет использоваться для рендеринга компонента. Элемент может быть создан с помощью функции React.createElement() , которая принимает имя компонента и его свойства в качестве аргументов.
  • Рендеринг элемента: После создания элемента, его необходимо рендерить на странице. Для этого необходимо вызвать функцию ReactDOM.render() , которая принимает созданный элемент и элемент DOM, на который будет рендериться компонент.
  • Обновление компонента: Компонент может быть обновлен в режиме реального времени, изменяя его состояние и свойства. При каждом изменении компонента, React перерендеривает его, чтобы отобразить изменения на странице.
  • Очистка памяти: После того, как компонент больше не нужен, его необходимо удалить из памяти, чтобы избежать утечек памяти. Для этого необходимо вызвать функцию ReactDOM.unmountComponentAtNode() , которая принимает элемент DOM, на который был рендерен компонент, и удаляет его из памяти.

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

Роутинг

хук useRoutes

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

Пример использования хука useRoutes:

import { useRoutes } from 'eact-router-dom';

function App() {
  const routes = {
    '/': <HomePage />,
    '/about': <AboutPage />,
    '/contact': <ContactPage />,
  };

  return useRoutes(routes);
}

В этом примере мы определяем три маршрута для нашего приложения: / , /about и /contact . Каждый маршрут соответствует компоненту, который будет отображаться на соответствующей странице. Затем мы передаем этот объект в хук useRoutes , который возвращает компонент, который отображает соответствующий компонент в зависимости от текущего пути.

Хук useRoutes позволяет легко определять маршруты для вашего приложения и упрощает процесс маршрутизации.