Практическое руководство по созданию многоразовых компонентов реакции

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

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

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

Эта статья предполагает базовые знания о ловушках React и React. Если вы хотите познакомиться с этими темами, рекомендую посмотреть “Начало работы с React“руководство и”Введение для взаимодействия крючков“.

Три основных показания для многоразового компонента реакции

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

Рекурсивное создание оболочек в одном стиле CSS

Мой любимый признак того, что я знаю, когда создавать повторно используемый компонент, – это частое использование одного и того же стиля CSS. Теперь вы можете подумать: «Погодите минутку: почему бы мне просто не присвоить одно и то же имя класса элементам, использующим один и тот же стиль CSS?» Вы совершенно правы. Не рекомендуется создавать повторно используемые компоненты каждый раз, когда некоторые элементы используют разные компоненты в одном и том же шаблоне. Фактически, это может привести к ненужным осложнениям. Поэтому вы должны задать себе еще один вопрос: являются ли эти элементы общим для дизайна крышки?

Например, рассмотрим следующие страницы входа и регистрации:


import './common.css';

function Login() {
  return (
    <div className='wrapper'>
      <main>
        {...}
      </main>
      <footer className='footer'>
        {...}
      </footer>
    </div>
  );
}

import './common.css';

function Signup() {
  return (
    <div className='wrapper'>
      <main>
        {...}
      </main>
      <footer className='footer'>
        {...}
      </footer>
    </div>
  );
}

Те же стили применяются к контейнеру (файл. <div> компонент) и нижний колонтитул каждого компонента. Итак, в этом случае вы можете создать два повторно используемых компонента – <Wrapper /> А также <Footer /> – И передать им детей в качестве опоры. Например, компонент входа в систему можно перестроить следующим образом:


import Footer from "./Footer.js";

function Login() {
  return (
    <Wrapper main={{...}} footer={<Footer />} />
  );
} 

В результате вам больше не нужно импортировать common.css На нескольких страницах или создайте одно и то же <div> Элементы оборачивают все.

Частое использование слушателей событий

Чтобы прикрепить прослушиватель событий к элементу, вы можете обработать его изнутри useEffect() как он:


import { useEffect } from 'react';

function App() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }

  useEffect(() => {
    document.addEventListener('keydown', handleKeydown);
    return () => {
      document.removeEventListener('keydown', handleKeydown);
    }
  }, []);

  return (...);
}

Или вы можете сделать это прямо внутри вашего JSX, как показано в следующем компоненте кнопки:


function Button() {
  return (
    <button type="button" onClick={() => { alert('Hi!')}}>
      Click me!
    </button>
  );
};

Если вы хотите добавить прослушиватель событий в document или window, вы должны следовать первому методу. Однако, как вы, возможно, уже поняли, для первого метода требуется больше кода с useEffect()А также addEventListener() А также removeEventListener(). Таким образом, в таком случае создание пользовательского хука позволит вашим компонентам быть более лаконичными.

Есть четыре возможных сценария использования прослушивателей событий:

  • Тот же прослушиватель событий, тот же обработчик событий
  • Тот же прослушиватель событий, другой обработчик событий
  • Другой прослушиватель событий, тот же обработчик событий
  • Другой прослушиватель событий, другой обработчик событий

В первом сценарии вы можете создать ловушку, в которой определены как прослушиватель событий, так и обработчик событий. Рассмотрим следующий крючок:


import { useEffect } from 'react';

export default function useKeydown() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }

  useEffect(() => {
    document.addEventListener('keydown', handleKeydown);
    return () => {
      document.removeEventListener('keydown', handleKeydown);
    }
  }, []);
};

Затем вы можете использовать эту ловушку в любом компоненте следующим образом:


import useKeydown from './useKeydown.js';

function App() {
  useKeydown();
  return (...);
};

Для трех других сценариев я бы рекомендовал создать ловушку, которая получает событие и функцию обработки событий в качестве свойств. Например, я закажу keydown А также handleKeydown В качестве скоб для моего нестандартного крючка. Рассмотрим следующий крючок:


import { useEffect } from 'react';

export default function useEventListener({ event, handler} ) {
  useEffect(() => {
    document.addEventListener(event, props.handler);
    return () => {
      document.removeEventListener(event, props.handler);
    }
  }, []);
};

Затем вы можете использовать этот хук в любом компоненте следующим образом:


import useEventListener from './useEventListener.js';

function App() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }
  useEventListener('keydown', handleKeydown);
  return (...);
};

Повторное использование одного и того же скрипта GraphQL

Когда дело доходит до повторного использования кода GraphQL, вам действительно не нужно искать теги. Для сложных приложений скрипты GraphQL для запроса или изменения легко занимают 30-50 строк кода, потому что есть много атрибутов для заказа. Если вы используете один и тот же скрипт GraphQL более одного или двух раз, я думаю, стоит его настраивать.

READ  Новые ноутбуки Alienware серии X - наш самый тонкий игровой ноутбук на сегодняшний день

Рассмотрим следующий пример:

import { gql, useQuery } from "@apollo/react-hooks";

const GET_POSTS = gql`
  query getPosts {
    getPosts {
    user {
      id
      name
      ...
      }
      emojis {
         id
         ...
      }
      ...
  }
`;

const { data, loading, error } = useQuery(GET_POSTS, {
  fetchPolicy: "network-only"
});

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

import { gql, useQuery } from "@apollo/react-hooks";

function useGetPosts() {
  const GET_POSTS = gql`{...}`;
  const { data, loading, error } = useQuery(GET_POSTS, {
    fetchPolicy: "network-only"
  });
  return [data];
}

const Test = () => {
  const [data] = useGetPosts();
  return (
    <div>{data?.map(post => <h1>{post.text}</h1>)}</div>
  );
};

Создание трех повторно используемых компонентов реакции

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

1. Компонент планирования

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

Рассмотрим следующий функциональный компонент React:


import React from "react";
import style from "./Feed.module.css";

export default function Feed() {
  return (
    <div className={style.FeedContainer}>
      <header className={style.FeedHeader}>Header</header>
      <main className={style.FeedMain}>
        {
          <div className={style.ItemList}>
            {itemData.map((item, idx) => (
              <div key={idx} className={style.Item}>
                {item}
              </div>
            ))}
          </div>
        }
      </main>
      <footer className={style.FeedFooter}>Footer</footer>
    </div>
  );
}

const itemData = [1, 2, 3, 4, 5];

Это типичная веб-страница с расширением <header>, а <main> а также <footer>. Если таких веб-страниц еще 30, вам легко надоест постоянно набирать HTML-теги и применять один и тот же стиль снова и снова.

В качестве альтернативы вы можете создать компонент макета, который получает <header>А также <main> А также <footer> В качестве реквизита, как в следующем коде:


import React from "react";
import style from "./Layout.module.css";
import PropTypes from "prop-types";

export default function Layout({ header, main, footer }) {
  return (
    <div className={style.Container}>
      <header className={style.Header}>{header}</header>
      <main className={style.Main}>{main}</main>
      <footer className={style.Footer}>{footer}</footer>
    </div>
  );
}

Layout.propTypes = {
  main: PropTypes.element.isRequired,
  header: PropTypes.element,
  footer: PropTypes.element
};

Этот компонент не требует <header> А также <footer>. Таким образом, вы можете использовать один и тот же макет для страниц независимо от того, есть ли у них верхний или нижний колонтитул.

READ  Новые ноутбуки Samsung Galaxy Book Go начинаются с 349 долларов и обеспечивают до 18 часов автономной работы

С помощью этого компонента макета вы можете превратить свою страницу канала в более сложный блок кода:


import React from "react";
import Layout from "./Layout";
import style from "./Feed.module.css";

export default function Feed() {
  return (
    <Layout
      header={<div className={style.FeedHeader}>Header</div>}
      main={
        <div className={style.ItemList}>
          {itemData.map((item, idx) => (
            <div key={idx} className={style.Item}>
              {item}
            </div>
          ))}
        </div>
      }
      footer={<div className={style.FeedFooter}>Footer</div>}
    />
  );
}

const itemData = [1, 2, 3, 4, 5];

Совет от профессионала по созданию макетов с липкими элементами

Многие разработчики склонны использовать файлы. position: fixed или position: absolute Когда они хотят вставить заголовок вверху области просмотра или нижний колонтитул внизу. Однако в случае с макетами этого следует избегать.

Поскольку элементы макета будут исходными элементами переданных реквизитов, вы хотите, чтобы стиль элементов макета был как можно более простым – чтобы проходил <header>А также <main>, или <footer> как предполагалось. Итак, рекомендую приложение position: fixed А также display: flex К внешнему элементу при планировании и подготовке overflow-y: scroll мне <main> пункт.

Это пример:


.Container {
  
  display: flex;
  flex-direction: column;

  
  width: 100%;
  height: 100%;

  
  overflow: hidden;
  position: fixed;
}

.Main {
  
  width: 100%;
  height: 100%;

  
  overflow-y: scroll;
}

Теперь давайте применим несколько стилей к вашей странице канала и посмотрим, что вы создали:


.FeedHeader {
  
  height: 70px;

  
  background-color: teal;
  color: beige;
}

.FeedFooter {
  
  height: 70px;

  
  background-color: beige;
  color: teal;
}

.ItemList {
  
  display: flex;
  flex-direction: column;
}

.Item {
  
  height: 300px;

  
  color: teal;
}

.FeedHeader,
.FeedFooter,
.Item {
  
  display: flex;
  justify-content: center;
  align-items: center;

  
  border: 1px solid teal;

  
  font-size: 35px;
}

Показать закрепленные верхний и нижний колонтитулы

Вот код в действии.

Вот так это выглядит на экранах рабочего стола.

Вот так это выглядит на мобильных экранах.

Mobile_Layout

Этот макет работает по назначению и на устройствах iOS! Если вы не знали, iOS печально известна тем, что вызывает неожиданные проблемы, связанные с местоположением, при разработке веб-приложений.

2. прослушиватель событий

Часто один и тот же прослушиватель событий используется в веб-приложении более одного раза. В таком случае рекомендуется создать собственный перехватчик React. Давайте узнаем, как это сделать, разработав файл useScrollSaver Хук, который сохраняет позицию прокрутки устройства пользователя на странице, поэтому пользователю не нужно снова прокручивать сверху. Эта ловушка будет полезна для веб-страницы, на которой перечислено большое количество элементов, таких как сообщения и комментарии; Представьте себе страницы каналов в Facebook, Instagram и Twitter без сохранения прокрутки.

Разберем следующий код:


import { useEffect } from "react";

export default function useScrollSaver(scrollableDiv, pageUrl) {
  
  const handleScroll = () => {
    sessionStorage.setItem(
      `${pageUrl}-scrollPosition`,
      scrollableDiv.current.scrollTop.toString()
    );
  };
  useEffect(() => {
    if (scrollableDiv.current) {
      const scrollableBody = scrollableDiv.current;
      scrollableBody.addEventListener("scroll", handleScroll);
      return function cleanup() {
        scrollableBody.removeEventListener("scroll", handleScroll);
      };
    }
  }, [scrollableDiv, pageUrl]);

  
  useEffect(() => {
    if (
      scrollableDiv.current &&
      sessionStorage.getItem(`${pageUrl}-scrollPosition`)
    ) {
      const prevScrollPos = Number(
        sessionStorage.getItem(`${pageUrl}-scrollPosition`)
      );
      scrollableDiv.current.scrollTop = prevScrollPos;
    }
  }, [scrollableDiv, pageUrl]);
}

Вы можете видеть, что файл useScrollSaver Хук должен получить два элемента: scrollableDiv, который должен быть прокручиваемым контейнером, как файл <main> контейнер в макете выше, и pageUrl, который будет использоваться в качестве идентификатора страницы, чтобы вы могли хранить позиции прокрутки для нескольких страниц.

Шаг 1. Сохраните позицию прокрутки

Прежде всего, вам нужно привязать прослушиватель событий scroll к вашему прокручиваемому контейнеру:

const scrollableBody = scrollableDiv.current;
scrollableBody.addEventListener("scroll", handleScroll);
return function cleanup() {
  scrollableBody.removeEventListener("scroll", handleScroll);
};

Теперь каждый раз scrollableDiv Он передается пользователем и называется функцией. handleScroll Он будет работать. В этой функции вы должны использовать любой из них localStorage или sessionStorage Чтобы сохранить позицию прокрутки. Разница в том, что данные в localStorage Срок его действия не истекает, пока данные находятся в sessionStorage Он сбрасывается, когда сеанс страницы заканчивается. вы можете использовать setItem(id: string, value: string) Чтобы сохранить данные в любом хранилище:

const handleScroll = () => {
  sessionStorage.setItem(
    `${pageUrl}-scrollPosition`,
    scrolledDiv.current.scrollTop.toString()
  );
};

Шаг 2. Восстановите положение прокрутки

Когда пользователь возвращается на веб-страницу, он должен быть направлен к предыдущей позиции прокрутки – если таковая имеется. Эти данные о местоположении в настоящее время сохранены в формате. sessionStorage, и вам нужно вынуть его и использовать. вы можете использовать getItem(id: string) Получить данные из хранилища. После этого вам просто нужно настроить scroll-top Из прокручиваемого контейнера до этого значения получаем:

const prevScrollPos = Number(
  sessionStorage.getItem(`${pageUrl}scrollPosition`)
);
scrollableDiv.current.scrollTop = prevScrollPos;

Шаг 3: используйте useScrollSaver Ссылка на любую веб-страницу

Теперь, когда вы закончили создание своего настраиваемого крючка, вы можете использовать его на любой веб-странице, если вы передадите в ловушку два обязательных элемента: scrollableDiv А также pageUrl. Вернемся к Layout.js И используй там свой крючок. Это позволит любой веб-странице, использующей этот макет, пользоваться вашим прокручиваемым буфером обмена:


import React, { useRef } from "react";
import style from "./Layout.module.css";
import PropTypes from "prop-types";
import useScrollSaver from "./useScrollSaver";

export default function Layout({ header, main, footer }) {
  const scrollableDiv = useRef(null);
  useScrollSaver(scrollableDiv, window.location.pathname);
  return (
    <div className={style.Container}>
      <header className={style.Header}>{header}</header>
      <main ref={scrollableDiv} className={style.Main}>
        {main}
      </main>
      <footer className={style.Footer}>{footer}</footer>
    </div>
  );
}

Пробная версия Scrollsaver

А вот код, работающий в песочнице. Попробуйте прокрутить страницу, а затем используйте стрелку в левом нижнем углу, чтобы перезагрузить приложение.

READ  Не спите на этом! Amazon снизила цены на свои самые популярные подушки из пены с эффектом памяти - только сегодня

Вы окажетесь там, где остановились!

3. Запрос / Мутация (только для GraphQL)

Если вы хотите использовать GraphQL с React, как это делаю я, вы можете еще больше сократить базу кода, создав ловушку React для запросов или мутаций GraphQL.

Рассмотрим следующий пример выполнения запроса GraphQL. getPosts():

import { gql, useQuery } from "@apollo/react-hooks";

const GET_POSTS = gql`
  query getPosts {
    getPosts {
    user {
      id
      name
      ...
      }
      emojis {
         id
         ...
      }
      ...
  }
`;

const { data, loading, error } = useQuery(GET_POSTS, {
  fetchPolicy: "network-only"
});

Если у серверной части будет запрашиваться все больше и больше атрибутов, скрипт GraphQL займет больше места. Итак, вместо того, чтобы повторять скрипт GraphQL и useQuery Каждый раз, когда вам нужно запустить запрос getPosts(), вы можете создать следующий перехватчик React:


import { gql, useQuery } from "@apollo/react-hooks";

export default function useGetPosts() {
  const GET_POSTS = gql`
  query getPosts {
    getPosts {
    user {
      id
      name
      ...
      }
      emojis {
         id
         ...
      }
      ...
  }
  `;

  const { data, loading, error } = useQuery(GET_POSTS, {
    fetchPolicy: "network-only"
  });

  return [data, loading, error];
}

После этого можно использовать файл useGetPosts() Подключите следующим образом:


import React from "react";
import Layout from "./Layout";
import style from "./Feed.module.css";
import useGetPosts from "./useGetPosts.js";

export default function Feed() {
  const [data, loading, error] = useGetPosts();
  return (
    <Layout
      header={<div className={style.FeedHeader}>Header</div>}
      main={
        <div className={style.ItemList}>
          {data?.getPosts.map((item, idx) => (
            <div key={idx} className={style.Item}>
              {item}
            </div>
          ))}
        </div>
      }
      footer={<div className={style.FeedFooter}>Footer</div>}
    />
  );
}

заключение

В этой статье я изучил три самых популярных указателя на повторно используемый компонент React и три самых популярных варианта использования. Теперь у вас есть знания когда Чтобы создать повторно используемый компонент React и Как делать Сделать это легко и профессионально. Вскоре вы обнаружите, что получаете удовольствие от рефакторинга строк кода в компонент React или усовершенствованный многоразовый хук. Используя эти методы восстановления, наша команда разработчиков Глина Он смог уменьшить нашу базу клинков до приемлемого размера. Надеюсь, ты тоже сможешь!

Leonid Morozov

Создатель. Любитель кофе. Любитель Интернета. Организатор. Выродок поп-культуры. Поклонник телевидения. Гордый кулинарa

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Наверх