NotesWhat is notes.io?

Notes brand slogan

Notes - notes.io

# В основном - основное.
# Зачем > синтаксис.

# JSX
- **array.map(...)**. Используем метод map для рендера массивов. **Не забываем про пропс key**. Похожие элементы, возможно, имеет смысл объединить в массив. (Например, для селекта имеет смысл собрать в массив все его опции, а не писать отдельно каждый тег <option>):
```javascipt
// bad
<select>
<option className="option">Value 1</option>
<option className="option">Value 2</option>
<option className="option">Value 3</option>
<option className="option">Value 4</option>
</select>

// good
const options = [ 'Value1', 'Value2', 'Value3', 'Value4' ];

<select>
{options.map((option) => <option key={ options } className="option">{ options }</option>)}
</select>
```
- **условный рендер**. Используем для рендера элементов, который отображаются не всегда, а в зависимости от какого-либо условия (напр. показать меню только если юзер нажал на соответствующую кнопку)
- **подъем состояния**. Т.к. данные в реакте передаются только от родителя детям (т.е. сверху вниз), и если, например, данные понадобились компоненту брату, то мы поднимает такие данные выше к их общему родителю и уже от родителя передаем сверху вниз.
```
const BrotherWithState = () => {
const [ data ] = useState(0);

return <span>{data}</span>
}

const Brother = () => {
return <span>I want your data, Comp1!</span>
}

const App = () => {
// если бы стейт был тут, то мы могли бы передать его пропсом в BrotherWithState и Brother

return (
<div>
<BrotherWithState />
<Brother /> // <= компонент Brother хочет получить доступ к состоянию компонента BrotherWithState. Но мы не можем передать даннве соседу или вверх, только вниз. Поэтому мы ПОДНИМАЕТ (переносим) стейт (хук) из компонента BrotherWithState в App и раздаем его сверху вниз компонентам BrotherWithState и Brother
</div>
);
}


// удалем стейт из BrotherWithState и переносим его в App
const BrotherWithState = ({ data }) => {
return <span>{data}</span>
}

const Brother = ({ data }) => {
return <span>I want your data, Comp1!</span>
}

const App = () => {
// переносим стейт в App и прокидываем как пропсы
const [ data ] = useState(0);

return (
<div>
<BrotherWithState data={ data } />
<Brother data={ data } />
</div>
);
}
```

# Компоненты
Компонент - функция, возвращающая разметку (html или null). Принимает один единственный аргумент объект, именуемый props.
- Классовый. Обладает стейтом и методами жизненного цикла.
- Функциональный. Реализует стейт и альтернативу методам жизненного цикла через хуки

- Управляемые - получают состояние из вне (через пропсы value и onChage)
- Неуправляемые - имеют свое собственное состояние. Можем получить их значение используя рефы.
```
// создаем рефу
const ref = useRef<HTMLInputElement>(null);

return (
<div>
// input - неупоавядемый компонент, т.к. не получает через пропсы value. Прокидываем рефу, чтобы получить доступ к инпуту
<input ref={ref} />
// через рефу получаем доступ к значению инпута
<button onClick={() => console.log(ref.current?.value)}>Click</button>
</div>
)
<input ref={ref} />

```

## Стейт или методы жизненного цикла?
- Стейт - переменная. Как и в любом ПО мы хотим иметь переменные. Но обычная переменная не вызывает перендер компонента (т.е. результат изменения не виден на ui). Поэтому используем концепцию стейта - переменная + функция setState, котороая изменяет переменную и при этом вызвает перендер. **Используем стейт для хранения данных внутри приложения** (Например открыто или закрыто меню, значение инпутов или форм ДО того как эти значения отправлены на бек, полученные данные с бека).
- Методы жизненного цикла - сайд эффекты. **Используются для взаимодейстия с внешним миром** (Например для получения и синхронизации данных с бека, отправки логов, взаиможействия локалстореджем и проч, webApi - timer)

# Методы жизненного цикла
Компонент проходит несколько фаз - впервые (или после удаления) **монтируется** => **обновляется** => **размонтируется** (удаляется).
На каждом из этих этапов мы можем выполнить какую-нибудь логику через специальные функции колбеки:
- componentDidMount - Срабатывает *после* монтирования компонента. Используется для первоночльного получения данных с бека (например, получения списка товаров в магазине)
- componentDidUpdate - Срабатывает *после* обновления компонента. Используется для (обновления) синхронизации с беком (например, после обновления поискового запроса юзера)
- componenWillUnmount - Срабатывает *перед* размонтированием (удаления) компонента. Используется для очистки ресурсов (например, если мы установили таймер, то его нужно очистить, чтобы освободить ресурсы браузера). Каждый раз когда мы подписываемся на ресурсы (websocket) или занимаем ресурсы (setTimer), устанавливаем соединение, то эти ресурсы надо освобить (отписаться, убрать таймер и тд.) в componenWillUnmount. Если вы выполняете какую-то логику и вы знаете что у нее есть обратное "освободлающее"- это почти всегда причина задуматься надо ли применять для этого componenWillUnmount.

- shouldComponentUpdate - Срабатывает *переда* обновлением компонента. Используется для оптимизации, вернув false мы предотвратим рендер компонента. Пример - с бека вам постоянно прилетают данные, парсив ответ бека через JSON.parse вы каждый раз получаете новый объект по ссылке (даже если данные не изменились). Если мы хотим чтобы компонет в такой ситуации рендерился не каждый раз, а только если данные действительно изменились, то мы можем выполнить проверку в shouldComponentUpdate (например изменился ли хеш или id ответа бекенда) и возвращать false, если данные не изменились и переренлер не нужен.

- static getDerivedStateFromError / componentDidCatch - Используется для создания ErrorBoundary (аналог try / catch для рендера компонента)

# Хуки
## Правила хуков:
- Можно вызывать только в функциональных компонентах и внутри других хуков
- Можно вызывать только верхнеуровнево (нельзя вызвать в циклах, в ифах, условнов ренедре и тд.)
- Называем с префикса use*

## Основные
- useState - аналог стейта классового компонента. Не забывакем про синтаксис колбека когда рассчитываем состояние на основе предыдущего. В отличие от классового у useState а) нет ограничения на кол-во стейтов в одном компоненте (можно сделать 2+ стейта) б) сотсояние поддерживает любой тип данных (не только объект), в) НО состояния объекты мы должны мержить самостоятельно при их частичном обновлении
```javascript
state = { name: 'alex', surName: 'bolduin' };

// class component - ok. можем частично обновить стейт
this.setStata({ name: 'Boris' }); // { name: 'Boris', surName: 'bolduin' }

// function component - bad - не можем частично обновить стейт
setStata({ name: 'Boris' }); // { name: 'Boris' }

// function component - ok - обязаны передать полный стейт при обновлении
setStata((prevState) => ({ ...prevState, name: 'Boris' })); // { name: 'Boris', surName: 'bolduin' }

```
- useEffect - альтернатива методам жизненного цикла классового компонента.
- функция ПОДПИСКИ (тело): срабатывает **ПОСЛЕ** ЛЮБОГО рендера (как после обновления так и после первого маунтинга) если нет массива зависимостей. Если массив есть - **ПОСЛЕ** каждого изменения любоцй зависимости из этого массива.
- функция ОТПИСКИ (возвращаемая): срабатывает **ПЕРЕД** любым перерендером (в тч перед удаление) - если нет массива зависимостей. Если массив есть - **ПЕРЕД** каждым перерендером по причине изменения любоцй зависимости из этого массива.
- массив зависимостей - ограничивает момент вызова. Отсутсвие массива зав = массив со всеми возможными зависимостями (т.е. будет выполняться на каждый рендер). Пустой массив = пустые зависимосимости (не от чего не зависим), useEffect отработает один раз (функция подписки - на первый рендер, а функция отписки - перед удплением. Т.е. с помощью пустого массива мы можем эмулировать метожы componentDidMount и componenWillUnmount)
```
useEffect(() => {
// функция подписки (тело функции) - срабатыва ПОСЛЕ рендера
const intervalId = setInterval(...)

// функция отписки (возвращаемая функция) - срабатыва ПЕРЕД перендером
return () => {
clearInterval(intervalId)
}
// массив завависимостей. Зависим от id - useEffect сработает на первый рендер и на каждое изменение id.
}, [ id ]);

useEffect(() => {
// пустой массив завависимостей, значит функция подписки сработает только 1 раз на манутнге - аналог componentDidMount
const intervalId = setInterval(...)

// пустой массив завависимостей, значит функция подписки сработает только 1 раз перед анмаунтом - аналог componenWillUnmount
return () => {
clearInterval(intervalId)
}
//пустой массив завависимостей
}, [ ]);


useEffect(() => {
// отсутствие массива завависимостей, значит функция подписки выполнится ПОСЛЕ КАЖДОГО рендера
const intervalId = setInterval(...)

// отсутствие массива завависимостей, значит функция подписки выполнится ПЕРЕД КАЖДЫМ перерендером
return () => {
clearInterval(intervalId)
}
// отсутствие массива завависимостей
});
```

## Второстепенные:
Функциональный компонент, в отличие от классов, не имеет инстанса, который живет между перерендерами, а значи не может сам по себе хранить значение между перерендерами. Следующие хуки помогают решить проблему **хранения значения между перерендерами**:
- useCallback - при каждом рендере в функциональном компоненте любая созданая функция хендлер это новая функция по ссылке. Используем хранения если надо **сохранить ссылку на функцию** с предыдущего рендера. !!! Любые зависимости функции должны быть в массиве зависимостей, иначе функция будет выполняться с с неактуальными значениями
```
const Button = ({ onClick }) => {
useEffect(() => {
console.log('хочу выполняться если onClick изменился');
console.log('но сразабатываю каждйы раз тк onClick по ссылке не равен');
fetch();
}, [ onClick ]);

return <button onClick />
}

const Parent = () => {
// handleClick на каждый рендер создается новый и по ссылке не равен старому, значит useEffect в компоненте Button срабатывает каждый раз
const handleClick = () => {}

// handleClickMemo из useCallback, по ссылке изменяется только когда изменится что-либо из массива зависомсотей (в данном сдлучае маасив пустой, те никогда не изм). А значи проблм useEffect компонента Button ушла, т.к. наш колбек между перерендерами по ссылке остается неизменным.
const handleClickMemo = useCallback(() => {}, []);

return <Button onClick={ handleClick } />
}

---

const Parent2 = () => {
const [ variable ] = useState(2);

const handleClickMemo = useCallback(() => {
// наша функция используем переменную variable, т.е. зависит от нее
console.log(variable);
// !!! любые зависимости функции должны быть в массиве зависимостей, иначе они будут с неактуальными значениями. Добавили переменную variable в массив.
}, [ variable ]);

return <Button onClick={ handleClick } />
}
```
- useMemo - любые вычисления (кроме тех что в useEffect) в функциональном компоненте выполняются на каждый рендер. Если эти вычисления ресурсоемкие, то с помощью useMemo мы можем запомнить результат таких вычислений с прошлого рендера и не пересчитывать их
```
// функция veryComplexFn очень ресурсоемакая и отрабаьтывает 2 сек
const veryComplexFn = () => {}

const Component = () => {
const [ state, setState ] = useState(0);

// каждый рендер вызывается veryComplexFn которая отрабатывает 2 сек, а значит наш комопнет рендерится 2 сек. А с учетом что он может рендерится очень часто, ui скорее всего просто повиснет.
const result = () => veryComplexFn();

// с помощью useMemo мы пересчитывает реззульатт толкьо при измении масива зависимостей. В данном случае массив пустой, а значит мы вызваем veryComplexFn толкьо 1 раз на самом старте
const resultMemo = useMemo(veryComplexFn, []);

// для useMemo работает тоже правило что и для остальных хуков - любые зависимости помещаем в массив зависммостей
const resultMemo = useMemo(() => veryComplexFn(state), [ state ]);

return <span>{ resultMemo }</span>
}
```
- useRef - возвращает рефу. В функциональных компонентах в ней также можно и нужно хранить любые значения, которые хотим сохранить между перерендерами, но изменение которых не должно приводить к новым перерендерам.

```
// Будем с помощью useRef считать кол-во перерендера компонента. Т.к. изменени счетчика не должно приводить к новым перендерам, то state на не подходит.
const CompoenntWithRenderCounter = () => {
const counter = useRef(0);

// counter из useRef сохраняет свое значение между перерендарами и мы можем хранить и изменять любые данные в его свойстве current,
counter.current++;
}
```

# Контекст
Иногда приходится передавать пропсы глубоко через десятки компонентов. Эта проблема называется property drill. Для ее решения используется контекст - альтернативный пропсам способ получения данных от родителя в обход промежуточных компонетов. Каждый раз когда хотим прокинуть пропсы в обход промежуточных комопнетов используем контектс.
Т.е. если у вас есть комопнет со стейтом и между ним и компонетом которому этот стейт нужен лежит 2-3 комопнета - то ок, можно прокинуть как обычно через пропсы через каждого. Но что если таких промежуточных комопнетов 20 50 100? Тогда чтобы не прокидывать данные через каждый такой промежуточный компонет используем контекс и передаем данные в обход их.

TODO добавить пример

# Важные правила при написании кода на React
- Если рассчитываем стейт на основе предыдущего, то используем синтаксис колбека для функции setState (в т.ч. для хуков)
- Не мутируем стейт и пропсы
- При паминге не забываем про уникальный пропс key

# Прочее
- Фрагменты - используем если нужно вернуть из компонента более одного корневого элемента
- Для оптимизации рендера можно использовать shouldComponentUpdate / pureComponent / React.memo
TODO пример React.memo
- Для перенаправления рефов используем React.forwardRef
```
const Component = () => {
// создаем рефу, чтобы получить доступ реальному DOM узлу
const ref = useRef<HTMLInputElement>(null);

const focuHandler = () => {
// т.к. нет друого способа вызвать focus у элемента, кро ме как вызвать одноименный метод на DOM узле
ref.current?.focus();
}

retunr (
<div>
// применяем рефу
<input ref={ ref } />
<button onClcik={ focuHandler }>Focus input</button>
</div>
)
}
----
// Все как в коде выше, но теперь input - наш собственный комопнент обертка. Тут проблема: ref - особый пропс и просто так к нему доступа нет. Чтобы получить к нему доступ и нужен forwarRef

// просто так доступа к пропсу ref не получить, тут ref всегда будет undefined
const Input = ({ ref }) => {
<input ref={ ref } />
}

// Оборачиваем компонент в React.forwardRef, теперь у нас есть доступ к ref и мы можем прокинуть его в нативный html
const Input = React.forwardRef((props, ref) => {
<input ref={ ref } />
})

const Component = () => {
const ref = useRef<HTMLInputElement>(null);

const focuHandler = () => {
ref.current?.focus();
}

retunr (
<div>
<Input ref={ ref } />
<button onClcik={ focuHandler }>Focus input</button>
</div>
)
}
```
- ErrorBoyndary - компонент с методами жизненного цикла static getDerivedStateFromError / componentDidCatch. Используется для отображения запасного ui, если в основном произошла ошибка в методе рендера. Аналог try / catch для компонентов.

     
 
what is notes.io
 

Notes.io is a web-based application for taking notes. You can take your notes and share with others people. If you like taking long notes, notes.io is designed for you. To date, over 8,000,000,000 notes created and continuing...

With notes.io;

  • * You can take a note from anywhere and any device with internet connection.
  • * You can share the notes in social platforms (YouTube, Facebook, Twitter, instagram etc.).
  • * You can quickly share your contents without website, blog and e-mail.
  • * You don't need to create any Account to share a note. As you wish you can use quick, easy and best shortened notes with sms, websites, e-mail, or messaging services (WhatsApp, iMessage, Telegram, Signal).
  • * Notes.io has fabulous infrastructure design for a short link and allows you to share the note as an easy and understandable link.

Fast: Notes.io is built for speed and performance. You can take a notes quickly and browse your archive.

Easy: Notes.io doesn’t require installation. Just write and share note!

Short: Notes.io’s url just 8 character. You’ll get shorten link of your note when you want to share. (Ex: notes.io/q )

Free: Notes.io works for 12 years and has been free since the day it was started.


You immediately create your first note and start sharing with the ones you wish. If you want to contact us, you can use the following communication channels;


Email: [email protected]

Twitter: http://twitter.com/notesio

Instagram: http://instagram.com/notes.io

Facebook: http://facebook.com/notesio



Regards;
Notes.io Team

     
 
Shortened Note Link
 
 
Looding Image
 
     
 
Long File
 
 

For written notes was greater than 18KB Unable to shorten.

To be smaller than 18KB, please organize your notes, or sign in.