DevKucher;

Как создать свой первый React Hook от начала до конца

Изображение на обложке для Как создать свой первый React Hook от начала до конца

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

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

Какую функцию мы хотим добавить?

Есть замечательный пакет react-copy-to-clipboard который позволяет копировать текст в буфер обмена одним кликом.

Copy to clipboard

Давайте сделаем похожый функционал но уже с помощью React хука.

Так как хуки созданы для повторного использования. Создадим отдельную папку с названием hooks, где и создадим новый файл с названием useCopyToClipboard.js. Внутри файла импортируем React и создаем одноименную функцию useCopyToClipboard.

Есть несколько способов скопировать текст в буфер обмена пользователя. Однако я предпочитаю использовать для этого библиотеку, которая делает процесс более надежным и называется copy-to-clipboard. Експортируем ее функцию copy.

// hooks/useCopyToClipboard.js
import React from "react";
import copy from "copy-to-clipboard";

export default function useCopyToClipboard() {}

Затем мы создадим функцию, которая будет использоваться для копирования любого текста, который нужно добавить в буфер обмена пользователя. Мы назовем эту функцию handleCopy.

Как сделать функцию handleCopy

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

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

import React from "react";
import copy from "copy-to-clipboard";

export default function useCopyToClipboard() {
  const [isCopied, setCopied] = React.useState(false);

  function handleCopy(text) {
    if (typeof text === "string" || typeof text == "number") {
      // copy
    } else {
      // don't copy
      console.error(
        `Cannot copy typeof ${typeof text} to clipboard, must be a string or number.`
      );
    }
  }
}

Затем нам нужно взять текст и преобразовать его в строку, которую мы затем передадим функции копирования. А сам хук возвращает handleCopy функцию которая как правило будет связана с нажатием кнопки.

import React from "react";
import copy from "copy-to-clipboard";

export default function useCopyToClipboard() {
  function handleCopy(text) {
    if (typeof text === "string" || typeof text == "number") {
      copy(text.toString());
    } else {
      console.error(
        `Cannot copy typeof ${typeof text} to clipboard, must be a string or number.`
      );
    }
  }

  return handleCopy;
}

Кроме того, нам нужно некоторое состояние, которое показывает, был ли текст скопирован или нет. Чтобы создать это, мы вызовем useState в верхней части нашего хука и создадим новую переменную состояния isCopied и сеттер setCopy.

Изначально это значение будет false. И будем его менять на соответствующий результат копирования. Если текст успешно скопирован - мы меняем его на true, в противном случае false.

Теперь мы возвращаем не только саму handleCopy но и состояние isCopied одним масивом, как это принято делать в хуках.

import React from "react";
import copy from "copy-to-clipboard";

export default function useCopyToClipboard(resetInterval = null) {
  const [isCopied, setCopied] = React.useState(false);

  function handleCopy(text) {
    if (typeof text === "string" || typeof text == "number") {
      copy(text.toString());
      setCopied(true);
    } else {
      setCopied(false);
      console.error(
        `Cannot copy typeof ${typeof text} to clipboard, must be a string or number.`
      );
    }
  }

  return [isCopied, handleCopy];
}

Как использовать useCopyToClipboard

Теперь мы можем использовать 'useCopyToClipboard' в любом компоненте. К примеру сделать сделаем вызов нашего хука при нажатии на кнопку. Добавляем onClick который вызывовет нашу функцию handleCopy с нужным текстом. Также в зависимости от состояния будем показывать разные иконки внутри кнопки.

import React from "react";
import ClipboardIcon from "../svg/ClipboardIcon";
import SuccessIcon from "../svg/SuccessIcon";
import useCopyToClipboard from "../utils/useCopyToClipboard";

function CopyButton({ code }) {
  const [isCopied, handleCopy] = useCopyToClipboard();

  return (
    <button onClick={() => handleCopy(code)}>
      {isCopied ? <SuccessIcon /> : <ClipboardIcon />}
    </button>
  );
}

Как добавить интервал сброса

Есть одно улучшение, которое мы можем внести в наш код. Поскольку мы уже написали наш хук, isCopied всегда будет true, то есть мы всегда будем видеть значок успешного копирования:

Before reset

Если мы хотим сбросить наше состояние через несколько секунд, мы можем сделать параметр для useCopyToClipboard который будет принимать время через которое состояние сброситься до дефолтного false. Давайте добавим эту функциональность.

Создаем параметр с именем resetInterval, значение по умолчанию которого равно null что гарантирует, что состояние не будет сброшено, если ему не будет передан аргумент.

Затем мы добавим useEffect, чтобы сказать, что если текст скопирован и у нас есть интервал сброса, мы делаем isCopied false после этого интервала с помощью setTimeout.

Кроме того, нам нужно очистить этот тайм-аут, если наш компонент, в котором используется хук, размонтируется.

import React from "react";
import copy from "copy-to-clipboard";

export default function useCopyToClipboard(resetInterval = null) {
  const [isCopied, setCopied] = React.useState(false);

  const handleCopy = React.useCallback((text) => {
    if (typeof text === "string" || typeof text == "number") {
      copy(text.toString());
      setCopied(true);
    } else {
      setCopied(false);
      console.error(
        `Cannot copy typeof ${typeof text} to clipboard, must be a string or number.`
      );
    }
  }, []);

  React.useEffect(() => {
    let timeout;
    if (isCopied && resetInterval) {
      timeout = setTimeout(() => setCopied(false), resetInterval);
    }
    return () => {
      clearTimeout(timeout);
    };
  }, [isCopied, resetInterval]);

  return [isCopied, handleCopy];
}

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

Финальный результат

В результате у есть хук который позволяет копировать текст и сбрасывать состояние если нужно после заданого интервала времени.

import React from "react";
import ClipboardIcon from "../svg/ClipboardIcon";
import SuccessIcon from "../svg/SuccessIcon";
import useCopyToClipboard from "../utils/useCopyToClipboard";

function CopyButton({ code }) {
  // isCopied is reset after 3 second timeout
  const [isCopied, handleCopy] = useCopyToClipboard(3000);

  return (
    <button onClick={() => handleCopy(code)}>
      {isCopied ? <SuccessIcon /> : <ClipboardIcon />}
    </button>
  );
}

Финальный результат

Я надеюсь что вы узнали что-то новое после прочтения этой статьи об React хуках, будете их создавать и использовать в собственных проектах.

Делитесь своим комментариями ниже и спасибо что дочитали до конца 💪🏻