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

Давайте начнем

Инициализируйте новое приложение с помощью create-response-app.

npx create-react-app react-hooks-tutorial

Не знаком с npx? "Прочитай это."

Войдите в каталог проекта и запустите приложение.

cd react-hooks-tutorial 
npm start

Если вы получаете ошибки о разрешениях, вам может потребоваться использовать sudo.
Возможно, вам также потребуется выбрать каталог приложения.

В этом руководстве будут использоваться хуки React для управления вращающимся логотипом React и другими аспектами шаблона App.js.

Измените направление вращения с помощью хука useState

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

Сначала импортируйте ловушку в App.js.

import React, { useState } from "react";

Затем мы создаем ловушку, которая объявляет переменную состояния spinClockwise и инициализирует ее значение равным true. Как часть объявления ловушки React создает метод для обновления новой переменной состояния.

const [spinClockwise, setSpinDirection] = useState(true);

Обновите animationDirection логотипа React, чтобы он устанавливался в зависимости от состояния нашего компонента.

<img
  src={logo}
  className="App-logo"
  alt="logo"
  style={{ animationDirection: spinClockwise ? "normal" : "reverse" }} 
 />

Под img добавьте кнопку, которая переключает направление вращения.

<button onClick={() => setSpinDirection(!spinClockwise)}>
    {spinClockwise 
      ? "Switch to counterclockwise" 
      : "Switch to clockwise"}
</button>

Вот как ваш App.js должен выглядеть на этом этапе.

import React, { useState } from "react";
import logo from "./logo.svg";
import "./App.css";

function App() {
  const [spinClockwise, setSpinDirection] = useState(true);
  return (
    <div className="App">
      <header className="App-header">
        <img
          src={logo}
          className="App-logo"
          alt="logo"
          style={{ animationDirection: spinClockwise ? "normal" : "reverse" }}
        />
        <button onClick={() => setSpinDirection(!spinClockwise)}>
          {spinClockwise ? "Switch to counterclockwise" : "Switch to clockwise"}
        </button>
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

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

Подробнее о useState здесь.

Настройте заголовок страницы браузера с помощью крючка useEffect

Надо импортировать крючок.

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

В компоненте, но за пределами оператора return, добавьте ловушку.

useEffect(() => {
    document.title = "React Hooks Tutorial";
}, []);

Проверьте заголовок обновленной страницы браузера, чтобы убедиться, что ловушка сработала.

Что тут происходит?

Вот объяснение хука useEffect из React docs ...

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

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

Подробнее о useEffect здесь.

Настройте ссылку «Learn React» с помощью useContext

Вы знаете, что делать.

import React, { useState, useEffect, useContext } from "react";

Вне функционального компонента App, но в том же файле объявите новый контекст, как в приведенном ниже коде.

const ExampleContext = React.createContext({
  text: "Learn React Hooks",
  href: "https://reactjs.org/docs/hooks-intro.html"
});

Теперь внутри нашего компонента получите значение ExampleContext с помощью ловушки useContext, которая принимает в качестве аргумента весь объект контекста.

const contextValue = useContext(ExampleContext);

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

<a
   className="App-link"
   href={contextValue.href}
   target="_blank"
   rel="noopener noreferrer"
 >
   {contextValue.text}
 </a>

Вот как ваш App.js должен выглядеть на этом этапе.

import React, { useState, useEffect, useContext } from "react";
import logo from "./logo.svg";
import "./App.css";

const ExampleContext = React.createContext({
  text: "Learn React Hooks",
  href: "https://reactjs.org/docs/hooks-intro.html"
});

function App() {
  const [spinClockwise, setSpinDirection] = useState(true);
  const contextValue = useContext(ExampleContext);

  useEffect(() => {
    document.title = "React Hooks Tutorial";
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        <img
          src={logo}
          className="App-logo"
          alt="logo"
          style={{ animationDirection: spinClockwise ? "normal" : "reverse" }}
        />
        <button onClick={() => setSpinDirection(!spinClockwise)}>
          {spinClockwise ? "Switch to counterclockwise" : "Switch to clockwise"}
        </button>
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href={contextValue.href}
          target="_blank"
          rel="noopener noreferrer"
        >
          {contextValue.text}
        </a>
      </header>
    </div>
  );
}

export default App;

Подробнее о useContext здесь.

Измените скорость вращения логотипа с помощью useReducer

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

import React, { useState, useEffect, useContext, useReducer } from "react";

Определите новую функцию-редуктор (вне функции компонента) с одним фрагментом состояния spinDuration (в секундах).

const initialState = { spinDuration: 2 };

function reducer(state, action) {
   switch (action.type) {
     case "increaseSpeed":
	return { spinDuration: state.spinDuration - 0.2 };
     case "decreaseSpeed":
	return { spinDuration: state.spinDuration + 0.2 };
     default:
        return state;
    }
}

Когда мы хотим increaseSpeed логотипа, мы должны уменьшить продолжительность вращения логотипа. Именно так работает свойство animation-duration css, которое мы скоро добавим в img.

Теперь, под тем местом, где мы поместили useContext, давайте создадим наши редукторы state и dispatch.

const [state, dispatch] = useReducer(reducer, initialState);
  • state - это объект, который мы инициализировали с помощью initialState, который имеет текущие значения свойств состояния.
  • dispatch - это функция, которая отправляет объекты (обычно называемые действиями) со свойством type, поэтому редуктор знает, какое обновление необходимо сделать дляstate.

Чтобы на самом деле отправлять действия и изменять скорость вращения логотипа, создайте две кнопки (под одной для переключения направления вращения).

<button
  disabled={state.spinDuration <= 0.4}
  onClick={() => dispatch({ type: "increaseSpeed" })}
>
  Increase speed
</button>
<button
  disabled={state.spinDuration >= 3}
  onClick={() => dispatch({ type: "decreaseSpeed" })}
>
  Decrease speed
</button>

Наконец, обновите логотип img, чтобы он animation-duration был привязан к state.

<img
 src={logo}
 className="App-logo"
 alt="logo"
 style={{
   animationDirection: spinClockwise ? "normal" : "reverse",
   animationDuration: `${state.spinDuration}s`
 }}
/>

Поиграйте с кнопками, и вы увидите, как логотип вращается все медленнее и быстрее. Вот и все с useReducer.

Эффективно повторно визуализируйте кнопку направления вращения с помощью useMemo

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

useCallback делает нечто подобное, но возвращает мемоизированную функцию обратного вызова, а не значение.

Из Википедии…

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

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

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

// ... no changes

const renderSpinDirectionButton = () => {
    console.log("RENDER BUTTON");
    return (
      <button onClick={() => setSpinDirection(!spinClockwise)}>
        {spinClockwise ? "Switch to counterclockwise" : "Switch to clockwise"}
      </button>
    );
};
  return (
    <div className="App">
      <header className="App-header">
        <img
          src={logo}
          className="App-logo"
          alt="logo"
          style={{
            animationDirection: spinClockwise ? "normal" : "reverse",
            animationDuration: `${state.spinDuration}s`
          }}
        />
       
       {renderSpinDirectionButton()}
        
// ... no changes

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

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

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

Нам нужно всего лишь две вещи для renderSpinDirectionButton функции, чтобы запоминать ее, иначе говоря, сделать ее повторной визуализацией только при изменении переменной состояния spinClockwise.

  1. Передайте функцию в useMemo в качестве первого аргумента.
  2. Укажите массив зависимостей в качестве второго аргумента в useMemo.

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

import React, { useState, useEffect, useContext, useReducer, useMemo } from "react";

// ... no changes

const renderSpinDirectionButton = useMemo(
    () => {
      console.log("RENDER BUTTON");
      return (
        <button onClick={() => setSpinDirection(!spinClockwise)}>
          {spinClockwise ? "Switch to counterclockwise" : "Switch to clockwise"}
        </button>
      );
    },
    [spinClockwise]
);

return (
// ... no changes

 {renderSpinDirectionButton}
 
// ... no changes

Взгляните на консоль браузера и обратите внимание, как кнопка повторно отображается при нажатии, иначе говоря, при изменении переменной spinClockwise. Мемоизация делает свое дело.

Вот и все!

Теперь вы знаете, как реализовать основы наиболее полезных хуков React.

Вот последний App.js

import React, {
  useState,
  useEffect,
  useContext,
  useReducer,
  useMemo
} from "react";
import logo from "./logo.svg";
import "./App.css";

const ExampleContext = React.createContext({
  text: "Learn React Hooks",
  href: "https://reactjs.org/docs/hooks-intro.html"
});

const initialState = { spinDuration: 2 };

function reducer(state, action) {
  switch (action.type) {
    case "increaseSpeed":
      return { spinDuration: state.spinDuration - 0.2 };
    case "decreaseSpeed":
      return { spinDuration: state.spinDuration + 0.2 };
    default:
      return state;
  }
}

function App() {
  const [spinClockwise, setSpinDirection] = useState(true);
  const contextValue = useContext(ExampleContext);
  const [state, dispatch] = useReducer(reducer, initialState);

  useEffect(() => {
    document.title = "React Hooks Tutorial";
  }, []);

  const renderSpinDirectionButton = useMemo(
    () => {
      console.log("RENDER BUTTON");
      return (
        <button onClick={() => setSpinDirection(!spinClockwise)}>
          {spinClockwise ? "Switch to counterclockwise" : "Switch to clockwise"}
        </button>
      );
    },
    [spinClockwise]
  );

  return (
    <div className="App">
      <header className="App-header">
        <img
          src={logo}
          className="App-logo"
          alt="logo"
          style={{
            animationDirection: spinClockwise ? "normal" : "reverse",
            animationDuration: `${state.spinDuration}s`
          }}
        />

        {renderSpinDirectionButton}
        <button
          disabled={state.spinDuration <= 0.4}
          onClick={() => dispatch({ type: "increaseSpeed" })}
        >
          Increase speed
        </button>
        <button
          disabled={state.spinDuration >= 3}
          onClick={() => dispatch({ type: "decreaseSpeed" })}
        >
          Decrease speed
        </button>
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href={contextValue.href}
          target="_blank"
          rel="noopener noreferrer"
        >
          {contextValue.text}
        </a>
      </header>
    </div>
  );
}

export default App;

Понравился этот урок?

Станьте еще более похожими на них, подписавшись на мой список рассылки.

Первоначально опубликовано на https://www.ryanjyost.com.