Создание сайтов в Юнокоммунаровске, ДНР. Практическое руководство по созданию повторно используемых компонентов React

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

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

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

В этой статье предполагается наличие базовых знаний о React и хуках React. Если вы хотите освежить в памяти эти темы, я рекомендую вам ознакомиться с руководством «Начало работы с React «и «Intorduction to React Hooks «.

Три основных показателя повторно используемого компонента React

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

Повторяющееся создание оберток с одним и тем же стилем CSS

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

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

// Login.js

import '. /common.css’;

function Login () {

return (

 

 

{... }

 

 

 

) ;

}

// SignUp.js

import '. /common.css’;

function Signup () {

return (

 

 

{... }

 

 

 

) ;

}

Те же стили применяются к контейнеру (

элементу) и нижнему колонтитулу каждого компонента. Таким образом, в этом случае вы можете создать два повторно используемых компонента — и
— и передать их дочерним элементам в качестве реквизита. Например, компонент входа в систему может быть реорганизован следующим образом:

 

// Login.js

import Footer from «. /Footer.js»;

function Login () {

return (

<Wrapper main={{... }} footer={

} />

 

) ;

}

В результате вам больше не нужно импортировать common.cssна несколько страниц или создавать одни и те же

элементы, чтобы обернуть все.

 

Повторяющееся использование прослушивателей событий

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

// App.js

import { useEffect } from 'react’;

function App () {

const handleKeydown = () => {

alert ('key is pressed.') ;

}

useEffect (() => {

document.addEventListener ('keydown’, handleKeydown) ;

return () => {

document.removeEventListener ('keydown’, handleKeydown) ;

}

}, []) ;

return (...) ;

}

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

// Button.js

function Button () {

return (

 

 

) ;

};

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

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

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

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

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

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

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

// useEventListener.js

import { useEffect } from 'react’;

export default function useKeydown () {

const handleKeydown = () => {

alert ('key is pressed.') ;

}

useEffect (() => {

document.addEventListener ('keydown’, handleKeydown) ;

return () => {

document.removeEventListener ('keydown’, handleKeydown) ;

}

}, []) ;

};

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

// App.js

import useKeydown from '. /useKeydown.js’;

function App () {

useKeydown () ;

return (...) ;

};

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

// useEventListener.js

import { useEffect } from 'react’;

export default function useEventListener ({ event, handler}) {

useEffect (() => {

document.addEventListener (event, props.handler) ;

return () => {

document.removeEventListener (event, props.handler) ;

}

}, []) ;

};

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

// App.js

import useEventListener from '. /useEventListener.js’;

function App () {

const handleKeydown = () => {

alert ('key is pressed.') ;

}

useEventListener ('keydown’, handleKeydown) ;

return (...) ;

};

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

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

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

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 (

 

{data?.map (post => {post.text}) }

 

) ;

};

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

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

1. Компонент макета

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

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

// Feed.js

import React from «react»;

import style from «. /Feed.module.css»;

export default function Feed () {

return (

 

 

 

Header

 

 

 

{

 

 

{itemData.map ((item, idx) => (

 

 

{item}

 

 

))}

 

 

}

 

 

 

Footer

 

 

 

) ;

}

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

Это типичная веб-страница с именами

, а
и, а
. Если таких веб-страниц будет еще 30, вы легко устанете постоянно писать HTML-теги и применять один и тот же стиль снова и снова.

 

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

и
в
качестве реквизита, как в следующем коде:

 

// Layout.js

import React from «react»;

import style from «. /Layout.module.css»;

import PropTypes from «prop-types»;

export default function Layout ({ header, main, footer }) {

return (

 

 

 

{header}

 

 

{main}

 

 

{footer}

 

 

 

) ;

}

Layout.propTypes = {

main: PropTypes.element.isRequired,

header: PropTypes.element,

footer: PropTypes.element

};

Этот компонент не требует

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

 

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

// Feed.js

import React from «react»;

import Layout from «. /Layout»;

import style from «. /Feed.module.css»;

export default function Feed () {

return (

<Layout

header={

Header
}

 

main={

 

 

{itemData.map ((item, idx) => (

 

 

{item}

 

 

))}

 

 

}

footer={

Footer
}

 

/>

) ;

}

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

Профессиональный совет по созданию макетов с липкими элементами

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

Поскольку элементы макета будут родительскими элементами передаваемых свойств, вы хотите, чтобы стиль ваших элементов макета был как можно более простым — чтобы переданные

,
или
были оформлены так, как предполагалось. Итак, я рекомендую применить position: fixedи display: flexк самому внешнему элементу вашего макета и установить overflow-y: scrollдля
элемента.

 

Вот пример:

/* Layout.module.css */

.Container {

/* Flexbox */

display: flex;

flex-direction: column;

/* Width & Height */

width: 100%;

height: 100%;

/* Misc */

overflow: hidden;

position: fixed;

}

.Main {

/* Width & Height */

width: 100%;

height: 100%;

/* Misc */

overflow-y: scroll;

}

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

/* Feed.module.css */

.FeedHeader {

/* Width & Height */

height: 70px;

/* Color & Border */

background-color: teal;

color: beige;

}

.FeedFooter {

/* Width & Height */

height: 70px;

/* Color & Border */

background-color: beige;

color: teal;

}

.ItemList {

/* Flexbox */

display: flex;

flex-direction: column;

}

.Item {

/* Width & Height */

height: 300px;

/* Misc */

color: teal;

}

.FeedHeader,

.FeedFooter,

.Item {

/* Flexbox */

display: flex;

justify-content: center;

align-items: center;

/* Color & Border */

border: 1px solid teal;

/* Misc */

font-size: 35px;

}

Демонстрация липкого верхнего и нижнего колонтитула

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

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

Desktop_Layout

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

Mobile_Layout

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

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

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

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

// useScrollSaver.js

import { useEffect } from «react»;

export default function useScrollSaver (scrollableDiv, pageUrl) {

/* Save the scroll position */

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]) ;

/* Restore the saved scroll position */

useEffect (() => {

if (

scrollableDiv.current &&

sessionStorage.getItem (`${pageUrl}-scrollPosition`)

) {

const prevScrollPos = Number (

sessionStorage.getItem (`${pageUrl}-scrollPosition`)

) ;

scrollableDiv.current.scrollTop = prevScrollPos;

}

}, [scrollableDiv, pageUrl]) ;

}

Вы можете видеть, что useScrollSaverхук должен получить два элемента: scrollableDiv, который должен быть прокручиваемым контейнером, таким же, как

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

 

Шаг 1: Сохраните положение прокрутки

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

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и воспользуемся твоим крючком. Это позволит любой веб-странице, использующей этот макет, пользоваться вашей заставкой прокрутки:

// 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 (

 

 

 

{header}

 

 

 

{main}

 

 

 

{footer}

 

 

 

) ;

}

Демо прокрутки

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

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

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:

// useGetPosts.js

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 () крючок следующим образом:

// Feed.js

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={

Header
}

 

main={

 

 

{data?.getPosts.map ((item, idx) => (

 

 

{item}

 

 

))}

 

 

}

footer={

Footer
}

 

/>

) ;

}

Заключение

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

Делитесь нашими материалами с друзьями!

 

 

Заказать разработку сайта