React Hooks и WebPython
React Hooks — это долгожданные дополнения в React 16.8, с радостью встреченные сообществом React.
Перед тем как рассматривать, что это такое, вспомним немного, как можно писать компоненты React.
import React from 'react'; class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
Данный компонент является тем, что называется
Такие stateless-компоненты можно переписать в стрелочном виде:
import React from 'react'; const Welcome = ({name}) => <h1>Hello, {name}</h1>;
Как можно увидеть, писать такие stateless-компоненты в стрелочном виде — огромное удовольствие. Но что делать с компонентами, которые имеют состояние? Например, с таким:
import React from 'react'; class Example extends React.Component { // Да, класс написан без наворотов babel — это для лучшего понимания ;) constructor(props) { super(props); this.state = { count: 0 }; } render() { const { count } = this.state; return ( <div> <p>You clicked {count} times</p> <button onClick={ () => this.setState({count: count + 1}) }> Click me </button> </div> ); } }
Обратите внимание, что для того, чтобы реализовать подобное состояние, необходимо:
- проинициализировать начальное значение в конструкторе;
- хранить этот count в state;
- получать значение этого count в render;
- и писать всё время
И это ещё простой пример! В действительности код может быть куда сложнее, как минимум, с вынесенным методом изменения состояния.
Разберёмся, как этот компонент переписать с помощью React Hooks:
import React, { useState } from 'react'; const Example = () => { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Обратите внимание, компонент написан в виде стрелочной функции.
useState
Итак,
// Данная функция принимает начальное значение поля и возвращает специальный массив ... = useState(0);
Что возвращает:
// useState возвращает массив из двух элементов — текущее состояние поля стейта // а ещё специальный метод — чтобы изменять состояние этого поля const [count, setCount] = useState(0);
Обратите внимание, что мы можем называть переменные для значения и метода, как хотим:
// Это всё корректно const [count, setCount] = useState(0); const [countValue, changeCount] = useState(0); const [c, updateCounter] = useState(0); const [userName, setUserName] = useState('Masha');
А дальше пользоваться этой переменной и методом можно вот таким образом:
<!-- Вот здесь просто вывели значение переменной --> <p>You clicked {count} times</p> <!-- А вот здесь просто вызываем в обработчике --> <button onClick={() => setCount(count + 1)}>
А что будет, если мы напишем несколько хуков?
const [count, setCount] = useState(0); const [userName, setUserName] = useState('Masha');
Как ни странно, state не склеит эти два поля, и они будут изолированы. Как useState определит, что это два разных поля? Это просто — по порядку вызова. Ну, т. е. первый вызов
Поэтому вводятся логичные практики: 1. Использовать хуки в корне компонента. 2. Не использовать хуки в циклах.
Собственно, всё. Подведём промежуточные итоги: 1. Мы теперь можем писать компоненты со state и в стрелочном виде. 2. useState инициализирует поле state начальным значением. 3. useState берёт на себя обязанности по хранению поля state. 4. useState оборачивает вызов setState и предоставляет более удобный метод для изменения значения. Поэтому компонент обновится.
Да и, разумеется, хуки нужно использовать только в функциональных компонентах.
useEffect и другие
Но и это не всё!
Если мы хотим делать что-то дополнительное при обновлении компонента, то классический способ выглядел бы следующим образом:
import React from 'react'; class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } // вызываем callback на начальном значении componentDidMount() { const { onNewValue } = this.props; onNewValue(this.state); } // вызываем callback при изменении значения componentDidupdate() { const { onNewValue } = this.props; onNewValue(this.state); } render() { const { count } = this.state; return ( <div> <p>You clicked {count} times</p> <button onClick={ () => this.setState({count: count + 1}) }> Click me </button> </div> ); } }
Обратите внимание, что помимо длинного кода, мы ещё и получили необходимость перегружать два метода:
Всё это можно объединить, используя хук
import React, { useState } from 'react'; const Example = ({onNewValue}) => { const [count, setCount] = useState(0); // Функция в аргументе вызовется на componentDidMount и componentDidUpdate useEffect(() => onNewValue(count)); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Помимо всего прочего —