Основная причина написания собственного хука — возможность повторного использования кода. Например, вместо того, чтобы писать один и тот же код для нескольких компонентов, использующих одну и ту же общую логику состояния (скажем, логику «setState» или localStorage), вы можете поместить этот код в пользовательский хук и повторно использовать его. Я покажу вам, как реализовать несколько полезных пользовательских хуков.

использоватьПредыдущий

Часто возникает вопрос: «При использовании хуков, как мне получить предыдущее значение свойства или состояния?». С компонентами класса React у вас есть метод componentDidUpdate, который получает предыдущие реквизиты и состояние в качестве аргументов, или вы можете обновить переменную экземпляра (this.previous = value) и ссылаться на нее позже, чтобы получить предыдущее значение. Итак, как мы можем сделать это внутри функционального компонента, у которого нет методов жизненного цикла или экземпляра для хранения значений? Крючки в помощь! Мы можем создать собственный хук, который использует внутренний хук useRef для хранения предыдущего значения. Смотрите рецепт ниже со встроенными комментариями.

import { useRef, useEffect } from "react";

export function usePrevious<T>(value: T): T | undefined {
  /* The ref object is a generic container whose current property is mutable
  and can hold any value, similar to an instance property on a class */
  const ref = useRef();

  // Store current value in ref
  useEffect(() => {
    ref.current = value;
  }, [value]); // Only re-run if value changes

  // Return previous value (happens before update in useEffect above)
  return ref.current;
}

// Ref: https://usehooks.com/usePrevious/

Использование

import React, { useState } from "react";

const prices = [100, 200, 300, 400, 500, 600, 700];

const Price = ({ price }) => {
  const prevPrice = usePrevious(price);
  const icon = prevPrice && prevPrice < price ? '😡' : '😊';

  return (
    <div>
      Current price: {price}; <br />
      Previous price: {prevPrice} {icon}
    </div>
  );
};

const Page = () => {
  const [price, setPrice] = useState(100);

  const onPriceChange = (e) => setPrice(Number(e.target.value));

  return (
    <>
      <select value={price} onChange={onPriceChange}>
        {prices.map((price) => (<option value={price}>{price}$</option>))}
      </select>
      <Price price={price} />
    </>
  );
}

использоватьTimeOut

Хук, чтобы легко использовать setTimeout(callback, delay), и он должен:

  1. Сбросьте таймер, если задержка изменится.
  2. НЕ сбрасывайте таймер, если изменяется только обратный вызов.
export function useTimeout(callback: () => void, delay: number) {
  const callbackFn = React.useRef(null)
  callbackFn.current = callback

  React.useEffect(() => {
    const timeoutId = setTimeout(() => {
      if (callbackFn.current) {
        callbackFn.current()
      }
    }, delay)

    return () => {
      clearTimeout(timeoutId)
    }
  }, [delay]) // Only re-run if delay changes
}

Использование

import { useState } from 'react'

export default function Component() {
  const [visible, setVisible] = useState(true)

  const hide = () => setVisible(false)

  useTimeout(hide, 5000)

  return (
    <div>
      <p>
        {visible
          ? "I'm visible for 5000ms"
          : 'You can no longer see this content'}
      </p>
    </div>
  )
}

Рекомендации

Другие статьи

https://medium.com/@lama.ibrahim96/в чем разница между ключами-в-и-объектами-6d872a3d526a

Спасибо за прочтение. Если вы узнали что-то новое из этой статьи, подпишитесь на меня 😊 Я буду публиковать больше материалов о React и Javascript.