React Flashcards
Назовите минимум 2 правила Хуков React
это один из продвинутых способов для повторного использования логики.
Это функция, которая на вход принимает одну компоненту, и выкидывает эту же компоненту, но обернутую в какую-то контейнерную компоненту.
Это нужно, чтобы переиспользовать логику и не дублировать код. В контейнерной компоненте сидит логика, и эту логику могут переиспользовать компоненты множество раз(компоненты, которые принимает та функция на вход)
С1 => function()=> C0(C1)(обернулась)
С2 => function()=> C0(C2)(обернулась)
С3 => function()=> C0(C3)(обернулась)
Компонент высшего порядка (Higher-Order Component, HOC) React (контейнерная компонента)
это один из продвинутых способов для повторного использования логики.
Это функция, которая на вход принимает одну компоненту, и выкидывает эту же компоненту, но обернутую в какую-то контейнерную компоненту.
Это нужно, чтобы переиспользовать логику и не дублировать код. В контейнерной компоненте сидит логика, и эту логику могут переиспользовать компоненты множество раз(компоненты, которые принимает та функция на вход)
С1 => function()=> C0(C1)(обернулась)
С2 => function()=> C0(C2)(обернулась)
С3 => function()=> C0(C3)(обернулась)
Примером может являться React.memo().
Она работает по принципу кеша - мы загрузили один раз с сервера картинку\аудио и он эту загрузку кеширует(сохраняет), чтобы снова использовать ее и не загружать с сервера повторно .
Как происходит с React.memo() - мы знаем, что при изменении компоненты, Реакт ее перерисовывает.
Если в компоненте находятся локальные стейты разных дочерних компонент, и один из этих локальных стейтов поменялся, то компонента перерисуется, перерисует дочернюю компоненту, стейт которого изменился + перерисует другую дочернюю компоненту, стейт которой НЕ менялся, те создатся новый виртуальный дом точно повторяющий старый виртуальный дом. И смысла в этом нет, плюс влияет на оптимизацию-долгая загрузка. Для этого есть вспомогательная функция высшего порядка (HOC), которая будет отслеживать приходят ли в компоненту новые данные-пропсы, которые изменят компоненту.
Для этого мы обворачивает компоненту, которая может перерисоваться без изменения стейта, в React.memo(UserSecret).
const UserSecret = ()=>{alert(‘Hi”)} -наша компонента
const User = React.memo(UserSecret) - обернули нашу компоненту UserSecret, создав новую - User.
Вызов компоненты происходит точно так же: <User></User>
В React.memo нужно оборачивать компоненты в 99% случаев.
Примечание: например, у дочерней компоненты вынесен в родительский компонент функция-коллбэк, например коллбэк OnClick-а вынесли в родит. компонент, то даже обернув дочернюю компоненту в useMemo, при изменении стейта в родит. компоненте, соответственно при перерисовки, дочерняя компонента все равно перересуется. Почему? Потому что при перерисовки коллбэк так же перерисуется и будет считаться за новую функцию. А эта новая функция пойдет через пропсы в дочернюю и будет считаться за обновленные данные, которые позволяют компоненте дочерней отрисоваться.
Решение - завернем коллбэк в useMemo:
const addBook = ()=>{…….} - наш коллбэк
const memoCallback = useMemo(()=>{ return addBook}, [book]) - завернули в useMemo и поставили зависимость, что если book поменяется, то выполнится эта функция. И эту функцию передаем теперь в пропсы в дочернюю компоненту.
Или есть такой вариант с useCallback: говорит, просто запомни эту функцию и возвращай всегда один и тот же коллбэк, пока не изменятся зависимости:
const memoCallback = useCallback(()=>{
// код коллбэка
}, [book])
https://github.com/ValeriyDyachenko/bxnotes/blob/master/content/lib/react/react-notes/rendering.md
Что такое React?
Реакт - библиотека т.к нужно доустанавливать Роутинг, Редакс и т.д по выбору.
Библиотеки React и React DOM предоставляют нам средства для создания UI. Мы сами их устанавливаем, используем только то, что нам нужно , контролируем их использование.
Angular - фреймворк, т.к дополнительно устанавливать ничего не требуется.
Внутри уже все есть нужное для работы и построения приложения
Грубо говоря библиотека это набор готовых решений, которые можно просто брать и использовать как захочется.
Фреймворк же накладывает опреденный стиль программирования, его элементы сильнее связаны между собой.
Точка входа - index.tsx. Там происходит render (root.render(<App></App>);) - сравнение двух виртуальных домов - старого и нового (что поменялось между ними).
render - это НЕ ОТРИСОВКА!!!!Как многие ошибочно думают. Вывод: render сначала сравнит 2 виртуальных дома, а затем перерисует изменения там, где они произошли.
Для чего нужен Реакт?
React нужен для эффективной отрисовки страницы приложения, для этого у него есть Виртуальный DOM. Виртуальный ДОМ сравнивается с ДОМом Браузера, если есть различия –заменяется. Перерисовка происходит не всей страницы, а конкретного узла, в котором найдены различия.
Как VDOM используется в Реакте?
В React каждая часть UI является компонентом и почти каждый компонент имеет состояние (state). При изменении состояния компонента, React обновляет VDOM. После обновления VDOM, React сравнивает его текущую версию с предыдущей(сравнивает старый Вирт.дом с новым)и ищет то, что поменялось. Этот процесс называется «поиском различий» (diffing).
После обнаружения объектов, изменившихся в VDOM, React обновляет соответствующие объекты в RDOM. Это существенно повышает производительность по сравнению с прямыми манипуляциями DOM. Именно это делает React высокопроизводительной библиотекой JavaScript.
https://habr.com/ru/companies/macloud/articles/558682/
Какой парадигме следует Реакт?
Реакт следует функциональной парадигме программирования.
Она базируется на чистых функциях - она не должна иметь побочных эффектов, она не должна изменять приходящие в нее данные, она может делать копию этих данных и уже можно модифицировать эту копию. Это называется Иммутабельность. Чистые функции всегда возвращают значение или другую функцию, а также не имеющие побочных эффектов.
Что такое чистые функции?
Функция - это объект, т.к имеет свои встроенные свойства: name, constructor и тд.
Чистая функция не имеет бизнес-логики, не работает с ДОМ, не посылает запросы на сервер (сайд эффект), просто получает данные(пропсы) и отображает их.
Side Effect - запрос на сервер, работа с DOM, изменение параметров функции.
Принципы чистой функции:
1. Иммутабельность - функция не -имеет права изменить данные, которые в нее пришли. Делаем копию данных.
2. Идемпотентность - возвращает один и тот же результат, когда она вызывается с тем же набором аргументов. Результат функции предсказуем.
3. Имеет return, чтобы что-то возвращать.
4. Никаких side effects. Изменение значений в глобальном мире, асинхронные запросы.
Чистая функция требуется к компонентам, редьюсерам и селекторам.
Что такое Принцип DRY?
5 принципов читаемого кода: KISS, YAGNI, DRY, BDUF и Бритва Оккама
DRY — Don’t Repeat Yourself (Не повторяйся).
React базируется на этом принципе - переиспользование компоненты, те UI необходимо создавать из переиспользуемых блоков.
Например, Создаем кнопку и подключаем ее, где и сколько раз необходимо. В плане логики здесь конечно также на помощь приходят вспомогательные функции (утилиты или хелперы) и хуки.
В целом,этот принцип говорит нам: -Избегать копирования кода
-Выносите общую логику
-Прежде чем добавлять функционал, проверить в проекте, может, он уже создан.
Что означает SOLID?
Принципы о том, чтобы сделать модули менее связанными друг с другом и обособленными.
Single responsibility - принцип единственной ответственности.
Одна сущность отвечает(инкапсулирует) за одну задачу. Можно сказать, что это декомпозиция.
Что решает?
-Когда одна сущность разрастается и отвечает за множество задач(антипаттерн GodObject), мы получаем много связанного кода, что-то ломается одно - ломается другое. С Декомпозицией вносить изменения стало проще.
-Ухудшается читаемость
Open-closed — принцип открытости / закрытости.
Расширение функционала должно происходить за счет написания новых классов/компонент(
т.е. если мы хотим расширить(дополнить) каким либо дополнением(
например, у браузера есть доп.расширения, которые можно установить, у VSCode доустановим какие-то плагины)), а не догрузки старых классов/компонент(т.е. мы не изменяем старый код у VSCode или браузера, а допрописываем новый код для расширения).
Итог: открыт для расширения, закрыт для модификации
К этому относится и один из принципов ООП - наследование. Т.е. мы создаем новый экземпляр уже существующего класса с какими-то характеристиками. Тем самым мы не дублируем код.
L - принцип подстановки Барбары Лисков
Наследуемый класс должен дополнять, а не замещать поведение базового класса.
Например, у базового есть определенные методы, затем создаем наследуемые классы, один из которых решил изменить один из методов базового класса. Это не то, что нужно. В таком случае лучше создать базовый без этого метода, и создать наследуемые классы, в одном из которых будет этот метод, чтобы потом от этого класса создать еще наследуемые классы, содержащие этот метод.
I - принцип разделения интерфейсов.
Пример:
Например, мы создали интерфейс Payments, который содержит в себе несколько методов payWebMoney();
payCreditCard();
payPhoneNumber();
Далее нам надо реализовать два класса-сервиса, которые будут у себя реализовывать различные виды проведения оплат (класс InternetPaymentService и TerminalPaymentService).
При этом TerminalPaymentService не будет поддерживать проведение оплат по номеру телефона. Но если мы оба класса имплементим от интерфейса Payments, то мы будем “заставлять” TerminalPaymentService реализовывать метод, который ему не нужен.
public class InternetPaymentService implements Payments{ //базовый
public void payWebMoney() {
//logic
}
public void payCreditCard() {
//logic
}
public void payPhoneNumber() {
//logic
}
}
public class TerminalPaymentService implements Payments{
public void payWebMoney() {
//logic
}
public void payCreditCard() {
//logic
}
public void payPhoneNumber() {
//??????? этот метод здесь не нужен!!
}
}
Таким образом произойдет нарушение принципа разделения интерфейсов.
Для того чтобы этого не происходило необходимо разделить наш исходный интерфейс Payments на несколько и, создавая классы, имплементить в них только те интерфейсы с методами, которые им нужны.
public interface WebMoneyPayment {
void payWebMoney();
}
public interface CreditCardPayment {
void payCreditCard();
}
public interface PhoneNumberPayment {
void payPhoneNumber();
} //разделили на три интерфейса вместо одного
public class InternetPaymentService implements WebMoneyPayment, CreditCardPayment, PhoneNumberPayment{
//взяли все то, что нужно этому классу!!
public void payWebMoney() {
//logic
}
public void payCreditCard() {
//logic
}
public void payPhoneNumber() {
//logic
}
}
public class TerminalPaymentService implements WebMoneyPayment, CreditCardPayment{
//взяли все то, что нужно этому классу!!
public void payWebMoney() {
//logic
}
public void payCreditCard() {
//logic
}
}
Что решает?
-код становится менее связанным
-получаем более предсказуемую работу
-избавляем программные сущности от неиспользуемых методов
D - принцип инверсии зависимости.
Модули верхнего уровня не должны зависеть от модулей нижнего уровня.
Пример:
это просто небольшой магазин, где оплата происходит только за наличные.
Создаем класс Cash и класс Shop.
public class Cash {
public void doTransaction(BigDecimal amount){
//logic
}
}
public class Shop {
private Cash cash;
public Shop(Cash cash) {
this.cash = cash;
}
public void doPayment(Object order, BigDecimal amount){
cash.doTransaction(amount);
}
}
мы уже нарушили принцип инверсии зависимостей, так как мы тесно связали оплату наличными к нашему магазину.
И если в дальнейшем нам необходимо будет добавить оплату еще банковской картой и телефоном (“100% понадобится”), то нам придется переписывать и изменять много кода. Мы в нашем коде модуль верхнего уровня тесно связали с модулем нижнего уровня, а нужно чтобы оба уровня зависели от абстракции.
Поэтому создадим интерфейс Payments.
public interface Payments {
void doTransaction(BigDecimal amount);
}
Теперь все наши классы по оплате будут имплементить данный интерфейс.
public class Cash implements Payments{
public void doTransaction(BigDecimal amount) {
//logic
}
}
public class BankCard implements Payments{
public void doTransaction(BigDecimal amount) {
//logic
}
}
public class PayByPhone implements Payments {
public void doTransaction(BigDecimal amount) {
//logic
}
}
Теперь надо перепроектировать реализацию нашего магазина.
public class Shop {
private Payments payments;
public Shop(Payments payments) { this.payments = payments; } public void doPayment(Object order, BigDecimal amount){ payments.doTransaction(amount); //выбрали способ картой } } Сейчас наш магазин слабо связан с системой оплаты, то есть он зависит от абстракции и уже не важно каким способом оплаты будут пользоваться (наличными, картой или телефоном) все будет работать.
Назовите хотя бы 2 диапазона HTTP статусов и что они означают?
Делаем запрос на сервер (request) , сервер возвращает ответ(объект-response). И в нем могут находится разные статусы:
200 - OK
300 - перемещение (301 - перенаправляет на новый сайт, он не работает, 302 - тех.работы, временное перемещение)
400 - ошибка клиента (404 - не найдено, данные неверны)
5xx - ошибка сервера.
Соотнесите типы HTTP-запросов с CRUD(create, read, update, delete) операциями.
Если мы хотим прочитать данные(R-read), то отправляем GET-запрос.
Добавить новые данные(С-create)- POST.
Изменить данные(U-update) - PUT.
В дополнение: чем отличается PUT от PATCH - если мы обновляем с помощью put, то мы отправляем на сервер ВСЮ сущность, даже если нам надо обновить одно свойство. patch - отправляем только то\те, свойства , которые хотим обновить, БЕЗ всей сущности.
Удалить данные(D-delete)- DELETE.
Что такое Reducer?
Это чистая функция, которая принимает стейт и action. Action - объект, т.е. как инструкция, которая говорит как именно пришедший стейт обработать\изменить. В нем обязательно содержится тип type и другие необходимые данные. По типу action-на ищется тот кейс, который необходимо выполнить для стейта
Для чего нужен импорт Реакта в файле jsx?
Компонента, которая содержит jsx-разметку будет ругаться без импорта Реакта. jsx считается расширением js. jsx не попадает в браузер, тк он его не понимает. он превращается благодаря траспилятору babel в js. При этом нам понадобится импорт Реакта
Что такое useMemo()
Что такое React.memo()
У функциональных компонентов есть проблема - весь код в теле выполняется при каждом обновлении.
useMemo - это хук, который сохраняет результат вызова функции (первый аргумент) и пересчитывает его только при изменении зависимостей (второй аргумент). useMemo возвращает результат вызова первого аргумента.
const result = useMemo(() => sum(num), [num]);
Он будет сравнивать поменялся ли num, если да, то вызовется первый параметр - sum(num) с новым num.
В useMemo мы оборачиваем только сложный код, который требует большого времени обработки, например, там есть глубокая вложенность.
Так же мы можем обернуть в useMemo и разметку, но если этого требуется, если, например, там вызываются сложные компоненты.
React.memo() компонент более высокого порядка. Он принимает компонент в качестве аргумента и запоминает результат. Мемоизированный результат обновляется только в случае изменения свойств исходного компонента.
Что такое TDD?
Test-driven development или процесс разработки через тестирование — это методология разработки программного обеспечения, которая основывается на повторении коротких циклов разработки: изначально пишется тест, покрывающий желаемое изменение, затем пишется программный код, который реализует желаемое поведение системы и позволит пройти написанный тест, а затем проводится рефакторинг написанного кода с постоянной проверкой прохождения всех тестов.
Чем отличаются SQL от noSQL базы данных?
SQL база данных - язык запросов, созданный для того, чтобы получать из базы данных необходимую информацию.
Схему работы SQL:
специалист формирует запрос и направляет его в базу. Та в свою очередь обрабатывает эту информацию, «понимает», что именно нужно специалисту, и отправляет ответ.
Данные хранятся в виде таблиц, они структурированы и разложены по строкам и столбцам, чтобы ими легче было оперировать. Такой способ хранения информации называют реляционными базами данных (от англ. relation — «отношения»). Название указывает на то, что объекты в такой базе связаны определенными отношениями.
Пример, MySQL
noSQL — это семейство нереляционных баз данных. В них разработчики отошли от использования традиционной табличной модели представления информации.
Нет единого стандарта: у каждой такой базы индивидуальный подход к записи, хранению и извлечению данных. Поиск информации может вестись, например, по парам «ключ — значение» или по наборам столбцов.
Пример, MongoBD. Она использует JSON-подобные документы с динамическими схемами, что облегчает хранение и запрос данных.
Что такое nodejs?
Браузер содержит движок v8, который может выполнять js. Со временем люди поняли, что js можно использовать не только для отображения UI в браузере, а еще и для того, чтобы делать запросы в БД, файловую систему(file system) и тд.
Движок v8 не может это все одновременно выполнять(single responsibility), его задача - парсить js и выполнять базовые функции, который он может делать, по этому решили создать альтернативную браузеру программу\платформу - Nodejs. Он так же использует движок v8, чтобы уметь понимать js.
Помимо того, что браузер содержит v8, который с js, он еще и привносит свои фишки - DOM, работа со стилями, ассинхронность - запросы на сервер с помощью fetch.
Итого, Nodejs - программа\платформа, построенная на движке v8, который может выполнять js. Для чего он нужен? С помощью написанных расширений, пакетов, он может работать с БД, с файловой системой, с сетевыми протоколами, можно построить сервис на нем
Нужен ли import React , когда мы пишем компоненту?
Так как браузеры не понимают JSX “из коробки”, разработчики полагаются на компиляторы типа Babel или Typescript, чтобы трансформировать JSX в обычный JS.
В React 17 Release Candidate появился новый, опциональный механизм трасформации JSX в JS.
Старая JSX трасформация работала следующим образом:
import React from ‘react’;
// обязательно с импортом Реакт
function App() {
return <h1>Hello World</h1>;
}
Трасформировался в:
import React from ‘react’;
function App() {
return React.createElement(‘h1’, null, ‘Hello world’);
}
Чтобы это решить в React 17 появляются две новые точки входа предназначенные для использования другими инструментами такими как Babel и Typescript и теперь вместо трансформации в React.createElement, импортируются и вызываются новые функции из пакета React.
Предположим ваш код выглядел вот так:
function App() { //теперь можно без импорта
return <h1>Hello World</h1>;
}
После новой трансформации он будет выглядеть вот так:
// Inserted by a compiler (don’t import it yourself!)
import {jsx as _jsx} from ‘react/jsx-runtime’;
function App() {
return _jsx(‘h1’, { children: ‘Hello world’ });
}
Новый механизм не импортирует React, хотя он всё ещё нужен для работы хуков.!!!
Как убрать неиспользуемые импорты React:
cd your_project
npx react-codemod update-react-imports
В результате:
-Удалятся все неиспользуемые импорты React
-Изменятся все импорты типа import React from «react» на именованные import { useState } from «react». Это предпочтительный способ импорта. Codemod не затронет импорты типа import * as React from «react», это тоже валидный импорт и в 17 версии он будет работать, но в дальнейшем мы будем просить избавляться от него
Что такое Babel в Реакте?
Babel — это компилятор, который преобразует ваш современный JavaScript для запуска в старых браузерах. Он также может выполнять и другие задачи, такие как преобразование синтаксиса JSX.
Это специальный скрипт, который позволяет при запуске веб-страницы в браузере на лету преобразовать весь содержащийся на ней код React в код javascript, понятный браузеру.
Babel преобразовывает JSX в js на этапе сборки-компиляции. Реакт на основе js должен создать Виртуальный Дом с объектами-узлами, после чего Реакт превратит эти объекты в объекты Реального Дома.
Из JSX в js - теги превращаются в document.createElement(…..).
Но уже этой функции нет, почему и можно не использовать Импорт Реакт, тк при компиляции с Babel, Babel делает импорт этой функции за нас.
Наш код:
function App() {
return <h1>Hello World</h1>;
}
Транспиляция по-новой:
import {jsx as _jsx} from ‘react/jsx-runtime’;
// импортнул за нас
function App() {
return _jsx(‘h1’, { children: ‘Hello world’ });
}
Что такое one-way binding и two-way binding?
Есть однонаправленный поток данных (one-way). Это означает, что одна часть кода может передавать в другую часть кода данные, а другая часть может получать и этот поток никак не меняется. В таком потоке данные «текут» от одного модуля к другому, а выходные данные предыдущего становятся входными следующего. Получается круговорот.
Яркий пример такой архитектуры потока - Редакс.
Например, пользователь нажимает кнопку-Представлении(UI) вызывает action, в котором содержится инфа что произошло. Action попадает в Диспатч и распространяет его по всем Редьюсерам, которые знают как его обработать. Редьюсеры преобразовывают данные в хранилище, тем самым это обновление данных влечет перерисовку Представления и цикл замыкается
Двунаправленный поток данных(two-way):
Данные в таком потоке могут передаваться в обе стороны.
Например, используется для связывания хранилища и представления, чтобы обновление, например, текста в поле ввода сразу обновило данные в хранилище.
Из плюсов:
Меньше кода, потому что не надо писать экшен и обработчик для него.
Из минусов:
Труднее отлаживать, когда двойное связывание используется для чего-то сложнее, чем обновление текста в поле ввода.
Фреймворки, например Вью И Ангуляр, которые используют двунаправленное связывание, часто реактивные — то есть применяют изменения мгновенно не только к UI, но и к вычисляемым данным.
Пример в Ангуляре - двустороннее связывание,
Является ли Реакт реактивным?
https://dev.to/balaevarif/pochiemu-react-nie-rieaktivien-40ig
В реактивном программировании при возникновении какого-либо события, например, клик на кнопку, или ответы из бэкенда, кэш, структуры данных, все это может быть представлено в виде ассинхронных потоков.
Поток - это последовательность событий, разворачивающихся во времени. Т.е. они идут друг за другом в зависимости раньше они появились другого или позже.
На эти потоки можно подписаться и реагировать на изменения.
Тот, кто создает событие, называется observer, а тот, кто получает (слушает) и обрабатывает его - subscriber.
Т.е. подписанные на поток subscriber-ы как-то обрабатывают его, когда событие возникло.
Реактивное программирование подразумевает реакцию на потоки событий, а не явный запрос какого-то действия от пользователя.
Что касается Реакта:
КАК React обрабатывает события и изменяет UI?
Если мы будем вызывать множество изменений state друг за другом, то React будет стараться их объединить и произвести меньше UI (DOM) изменений.
Всё это сделано, чтобы оптимизировать изменения DOM элементов.
React “планирует” изменения UI. Они могут быть не равны количеству поступающих событий.
Отсюда теряется понятие потоковости и “реактивности”. Это очень хорошо заметно, если попробовать реализовать с помощью React приложение, основанное на системе постоянных событий (например, реальновременной графики), которые “пушатся” извне.
Что происходит на Реакте, когда пользователь что-то сделал на UI, например кликнул по кнопке?
Процесс обновления UI в React приложении состоит из трех фаз: (что-то произошло на UI)
1.Render — формирование текущего дерева компонентов на основании их свойств (props) и состояния (state).
Render не перерисовывает DOM дерево, его задача — формирование “виртуального” дерева.
2.Reconciliation(Согласование) — сравнение и вычисление отличий с предыдущим деревом.
Если полученное дерево совпадает с предыдущим, то обновления DOM не произойдет.
3.Commit — обновление DOM
- Render:
Срабатывает событие-коллбэк, который приведет к изменению стейта(useState, useReducer)(или например изменение приходящих пропсов), происходит ре-рендер компоненты-функция перевызывается. Если в ней сидят дочерние компоненты, они тоже будут перевызываться. Далее работает Babel-транспилирует jsx код в js. Теперь появляется Новый Виртуальный Дом.
2.Reconciliation:
Новый Виртуальный Дом сравнивается со старым Виртуальным Домом, сравниваются объекты(узлы ДОМ-а) старого ВД и нового ВД, где появились\заменились на новые объекты-узлы.
3.Commit:
Внесение этих изменений в Реальный Дом.
Как происходит первичный рендеринг?
Render — формирование текущего дерева компонентов на основании их свойств (props) и состояния (state).
При первом запуске приложения, в index.js необходимо вызвать рендеринг с помощью вызова createRoot(document.getElementById(‘root’)) c полученным корневым Дом-элементом-узлом из index.html.
Затем происходит первичный рендер-вызов корневой компоненты -
т.е. вызываем методом .root(<App></App>)
Таким образом происходит вызов корневой компоненты и каскадом ниспадает на остальные компоненты, получая от всех jsx-разметку, затем с помощью Babel транспилируется в js(раньше было с помощью React.createElement), с помощью которого Реакт создает Виртуальный Дом
Рендерится корневая компонента, подтягивая дочерние. Они возвращают jsx-разметку, Babel транспилирует в js, формируется Виртуальный Дом.
Затем происходит отрисовка в браузере.
Если у нас функция вне компоненты возвращает какие-то тяжелые большие вычисления, вызываем ее в нашей компоненте и эти вычисления мы используем в качестве инициализационного значения в useState, то как избежать того, чтобы при изменении стейта функция эта не перевызывалась каждый раз? Ведь будут происходить тяжелые вычисления каждый раз, что повлияет на оптимизацию.
Можно вызов этой функции завернуть в useMemo и сделать зависимость пустой, чтобы она вызывалась только один раз.
function hardDate(){ // функция с тяжелыми выч.
……
return 34488365536536
}
function State(){ // наша компонента
const initialValue = useMemo(hardDate, [])
// завернули в useMemo, вызов один раз
const [counter, setCounter] = useState(initialValue )
//попавшее сюда первый раз инициализационное значение так и будет сидеть, несмотря на то, что функция могла бы каждый раз перезапускаться без useMemo.
// каждый раз это число будет увеличиваться на 1, функция уже не будет перезапускаться.
return
<>
<button onClick={()=>setCounter(counter+1)}>INC</button>
</>
}
Но useState сам может оптимизировать:
вместо инициализационного значения из переменной, передадим функцию ему и он возьмет в качестве инициализационного значения то, что она вернет:
const [counter, setCounter] = useState(hardDate)
useState вызовет эту функцию ОДИН раз и возьмет ее значение. Например, если мы вдруг захотим взять counter, который якобы хранит эту функцию, то не получится, т.к. он ее не хранит, а просто вызывает и забирает значение.
так же setCounter может принимать не только какое-то значение, а функцию, которая по каким-то правилам будет изменять стейт.
const inc = (state: number)=>{
return state+1
}
return
<>
<button onClick={()=>setCounter(inc )}>INC</button>
</>
// укороченный вариант
<button onClick={()=>setCounter((state: number)=> return state+1)}>INC</button>
Т.е. мы можем использовать функцию в setCounter , когда нужно обновить состояние основываясь на текущих данных, которые в данный момент находятся в состоянии.
Это функция, которая принимает предыдущее состояние и возвращает новое состояние
Что такое useEffect?
Это Хук для работы с сайд эффектами такими как запрос на сервер, setTimeout, getElementById, document.title.
Важно запомнить - сначала происходит отрисовка контента, потом запускается коллбэк в useEffect, т.е. первая отрисовка - функция-компонента вызывается , например, инициализируется переменная, фиксируется useEffect, но он не запускается, оставляет на потом, Реакт получает от компоненты jsx, отрисовывает, а потом после всего этого запускает коллбэк useEffect-а.
Вывод: сначала должно что-то отобразиться в Представлении, а потом уже запускается коллбэк.
Например, при повторном рендеренге - нажимаем на кнопку, чтобы увеличить счетчик,стейт изменился, перевызвалась функция, увеличенный счетчик отобразиться в Представлении, а уже только потом запустится коллбэк useEffect.
Зависимости:
[] - будет запущен при первичном рендеренге
[counter] - будет перезапускаться каждый раз при изменения стейта - counter
нет зависимости - будет запускаться каждый раз при ре-рендеринга компоненты
Что такое clean up в useEffect?
Помним, что useEffect будет вызван только тогда, когда будет отрисован контент(оч кратко: Реакт возьмет jsx, создаст Вирт.Дом, затем Реал.Дом).
После useEffect может образовывать множество мусора в виде setTimout, задержанного ответа от сервера, подписка на какой-то ДОМ-элемент и тд. тогда, когда компонента уже будет размонтирована(например, произойдет ре-рендер компоненты). По этому внутри коллбэка в конце ставится функция clean up
Пример:
return ()=> clearInterval(id).
При пустой зависимости [], когда useEffect запускается один раз при рендере компоненты, то побочные сайды очищаются при демонтировании компоненты.
Когда есть зависимость [counter], то useEffect запускается когда меняется стейт, тем самым происходит ререндер компоненты, эта самая очистка очищает то, что было ДО ререндера компоненты, т.е. если у нас в локальном стейте сидел 0 и он увеличился на 1, то очистка будет происходить с нулем, с данными, которые были ДО ререндера.
Зачем нужна функция React.createContext и примеры ее использования?
React Context - способ управления состоянием, заменив Redux.
Вообще Контекст разработан для передачи данных, которые можно назвать «глобальными» для всего дерева React-компонентов .
Этот объект Context создается с помощью React.createContext отдельно:
const MyContext = React.createContext({} as StoreType)
Дальше какая-то из компонент может быть Провайдером этого контекста и положить props - данные, например, стор:
<MyContext.Provider store={props.store}>
Далее этот пропс-стор позволяет использовать дочерние компоненты-потребители-сonsumer-ы Провайдера.
<MyContext.Consumer>
{value => /* отрендерить что-то, используя значение контекста */}
</MyContext.Consumer>
Все потребители, которые являются потомками Provider, будут повторно рендериться, как только проп value у Provider изменится.
То есть не нужно теперь передавать по дереву иерархии передавать через пропсы там, где не нужно.
Итог: контекст нужен для того, чтобы пробрасывать дочерним компонентам что-то глобальное\общее, что нужно всем компонентам - тема, язык, настройки, хранилище-стейт, роутинг
Что такое условный рендеринг и первичный\начальный\инициализационный рендеринг?
Условный рендеринг - можно условно выводить JSX, используя синтаксис JavaScript, такой как операторы if, && и ? :.
Первичный рендеринг:
Когда приложение запускается, нам необходимо вызвать начальный рендеринг.
Он выполняется вызовом createRoot с корневым узлом DOM, а затем вызовом его метода render с нашим компонентом:
const root = createRoot(document.getElementById(‘root’));
root.render(<App></App>);
После запуска рендеринга React вызывает наши компоненты, чтобы определить, что отобразить на экране. “Рендеринг” - это обращение React к вашим компонентам. При первом рендере React вызывает корневой компонент - App.
И спускается каскадом вниз по компонентам, они возвращают jsx разметку, которую Babel транспилирует в js, затем на основе этого js, React создаст DOM-узлы для тегов <section>, <h1> и трех <img></img>(пример всех тегов из всех компонент) которые будут находится в Реальном Доме.
Затем происходит браузерный рендеринг - браузер отрисовывает экран.
https://www.youtube.com/watch?v=rsW9_UtF4jk
// Virtual DOM
Что такое батчинг?
Батчингом в React называют процесс группировки нескольких вызовов обновления состояния в один этап ререндера.
Это положительно сказывается на производительности.
Т.е. Реакт ждет, ждет, пока не будет выполнен весь код в обработчиках событий, прежде чем обрабатывать ваши обновления состояния.
Только после выполнения будет происходить ОДИН ре-рендер, а не три.
<button
onClick={() => {
setNumber(number + 1);
setNumber2(number + 3);
setNumber3(number + 4);
}}
>
+3
</button>
Так же Реакт добавил возможность обновления состояний для асинхронных операций: promise, таймауты, fetch запросы:
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React будет вызывать ререндер только один раз, в конце
}, 1000);
Что отличает Flux от Redux?
Основные отличия между Flux и Redux заключаются в том, что Flux – это архитектурный шаблон, в то время как Redux – это библиотека, которая строится на идеях Flux.
Redux также добавляет новые возможности и использует иммутабельные объекты для управления состоянием.
Flux – архитектурный шаблон, разработанный в Facebook, который описывает, как управлять состоянием приложения.
Он состоит из четырех компонентов: View-Компонента, Action, Dispatcher-контроллер, который отвечает за обработку входящих экшенов, и Store-Хранилище. Причем этих сторов может быть несколько.
Основной идеей Flux является однонаправленный поток данных, где данные изменяются только через Action и могут быть получены View только от Store.
Redux – библиотека, которая упрощает работу с управлением состояния. Она использует идеи Flux, но добавляет новые возможности …
В Redux так же используется однонаправленный поток данных, но имеет отличие от Flux-архитектуры потоков данных. Так же у Редакс может быть только один стор.
Еще основное отличие - использование иммутабельных объектов. Значения состояния(стор) не могут быть изменены напрямую, они могут быть изменены только путем создания нового объекта. {…store}.
И еще одна отличие в архитектуре потоков данных:
Flux имеет строго определенную однонаправленную структуру данных, которая включает в себя хранилище состояний (store), действия (actions) и диспетчер (dispatcher). Каждое action-действие отправляется через dispatcher-диспетчер, который распределяет его на соответствующий метод В store.
Redux, с другой стороны, использует паттерн Redux cycle, который состоит из трех основных элементов: действия (actions), редюсеры (reducers) и хранилище (store).
Еще отличие:
Во Flux изменение состояния происходит путем создания действий и передачи их в хранилище, после чего они распределяются на все компоненты, которые подписались на эти изменения.
У Redux в отличие от Flux, изменение состояния происходит с помощью чистых функций-редюсеров. Когда действие передается в хранилище, оно передается в соответствующий редюсер, который изменяет состояние и возвращает новый объект с обновленными данными. Компоненты получают новое состояние через подписку на хранилище.(useSelect подписывается на стор)