React Hooks

Хуки появились в реакте в феврале 2019 года начиная с версии 16.8 и быстро стали популярными.
Их преимущества:
- позволяют отказаться от классов, которые чуть сложнее в реализации и чуть медленнее в быстродействии
- в некоторых случаях позволяют сделать код проще
- избавляемся от this, которое в некоторых случаях может работать с ошибкой (вместо пользователя Боб письмо получит Алиса, что недопустимо)

Хуки позволяют в функциональных компонентах хранить state, иметь доступ  к жизненному циклу компонентов, использовать контекст.
Если хуки использовать правильно, они существенно упрощают структуру приложения.

Чтобы использовать хуки, их нужно импортировать из пакета react

import React, { useState } from 'react';

Пример кода с хуками

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

const HookSwitcher = () => {
  const [color, setColor] = useState("gray");
  const setRed = () => setColor("red");
  const setBlack = () => setColor("black");

  return (
    <>
      <div style={{ padding: "40px 10px", backgroundColor: color }} />
      <button onClick={setRed}>Red</button>
      <button onClick={setBlack}>Black</button>
    </>
  );
};

ReactDOM.render(<HookSwitcher />, document.getElementById("root"));

Хук useState 

Хук useState заменил обычный state в классовом компоненте

Рассмотрим хук

const [ color, setColor ] = useState('#c0c0c0');

Он содержит
- переменную color
- функцию для изменения этой переменной setColor
- значение по умолчанию '#c0c0c0'

Чтобы изменить значение переменной color по клику, вызываем функцию

onClick={() => setColor('#ff6666')}

Если нужно изменить свойство fontSize на основе предыдущего, недостаточно просто вызвать функцию setFontSize с нужным значением
нужно прописать функцию, получающую предыдущее значение и меняющую его

const [ fontSize, setFontSize ] = useState(18);
onClick={() => setFontSize(fontSize => fontSize + 2)}

Если в state можно было поменять только одно поле объекта, а остальные оставались неизменными, в useState объект переписываются полностью

Т.е, если в useState был объект, и нам нужно в нём поменять только одно свойство, остальные придётся переписать, иначе они сотрутся
Но есть вариант использовать деструктуризацию

const [person, setPerson] = useState{firstName: 'Bob', lastName: 'Smith'}
setPerson(person => {...person, firstName: 'Alisa'})

Отличия useState от state
1. Стейт один, useState может быть много
2. В стейте можно менять отдельные свойства, в useState нужно записывать все свойства, в том числе и те, которые не изменились


useContext


useContext заменил (Higher-Order Component, HOC) - компоненты высшего порядка

Возможность передачи контекста - ещё одно преимущество хуков, которые позволяют сократить количество кода

чтобы передать два значения контекста в HOC понадобилось бы создать пять компонентов:
обёртка => контекст => обёртка => контекст => компонент, который эти контексты использует

При помощи хуков передача контекста очень упрощается

import React, { useContext } from 'react';

const MyContext = React.createContext();

const App = () => {
  return(
    <MyContext.Provider value='Hello useContext' >
      <Child />
    </MyContext.Provider>
  );
}

const Child = () => {
  const value = useContext(MyContext);
  return(
    <p>{value}</p>
  );
}

export default App;


Хук useEffect

Хук useEffect позволяет создавать "side effects" (побочные эффекты), например, извлечение данных, ручное изменение структуры DOM, использование таймеров. В нём можно делать подписки, отправлять запросы управлять анимацией 

Он служит той же цели, что методы жизненного цикла componentDidMount, componentDidUpdate и componentWillUnmount 

В качестве параметра в useEffect() передается функция, которая определяет "эффект", который затем применяется в приложении 

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

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

const Counter = ({value}) => {
  useEffect(() => {console.log('useEffect()')}, [value])
  return(
    <p>{value}</p>
  )
}

const App = () => {

  const [value, setValue] = useState(0);
  const [visible, setVisible] = useState(true);

  if(visible) {
    return(
      <div>
        <button onClick={() => setValue(value => value + 1)}>+</button>
        <button onClick={() => setVisible(false)}>hide</button>
        <Counter value={value}/>
      </div>
    );
  } else {
    return(
      <button onClick={() => setVisible(true)}>visible</button>
    )
  }  
}

export default App;