Русский
Русский
English
Статистика
Реклама

Перевод Простые советы по написанию чистого кода React-компонентов

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



Избегайте использования оператора spread при передаче свойств


Начнём с анти-паттерна, с приёма работы, которым лучше не пользоваться в тех случаях, когда для этого нет конкретных, обоснованных причин. Речь идёт о том, что следует избегать использования оператора spread ({...props}) при передаче свойств от родительских компонентов дочерним.

Передача свойств таким образом может, на самом деле, ускорить разработку. Но этот приём значительно усложняет обнаружение ошибок в коде. Разработчик начинает меньше доверять своим компонентам, это усложняет их рефакторинг, и, как результат, ошибки появляются в них раньше, чем в случаях, когда оператор spread для передачи свойств не используется.

Объявляйте функции, использующие несколько параметров, применяя объекты с параметрами


Если функция принимает несколько параметров хорошо будет поместить их все в объект. Вот как это может выглядеть:

export const sampleFunction = ({ param1, param2, param3 }) => {console.log({ param1, param2, param3 });}

Если подходить к созданию сигнатур своих функций именно так можно получить несколько заметных преимуществ перед обычным способом объявления функций:

  1. Вам больше не придётся беспокоиться о порядке, в котором функции передаются аргументы. Я несколько раз сталкивался с проблемой, когда передача аргументов функции в неправильном порядке приводила к возникновению ошибки.
  2. При работе с редакторами, в которых используется IntelliSense (в наши дни это практически все редакторы), в нашем распоряжении окажется приятная возможность автодополнения при вводе аргументов функции.

При работе с обработчиками событий используйте функции, возвращающие функции-обработчики


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

Взгляните на этот пример:

export default function SampleComponent({ onValueChange }) {const handleChange = (key) => {return (e) => onValueChange(key, e.target.value)}return (<form><input onChange={handleChange('name')} /><input onChange={handleChange('email')} /><input onChange={handleChange('phone')} /></form>)}

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

Используйте объекты, хранящие пары ключ-значение вместо условного оператора


Если нужно выводить различные элементы, основываясь на некоей логике, я посоветовал бы пользоваться объектами, хранящими данные в формате ключ-значение (коллекциями записей), а не выражениями if/else.

Вот пример, в котором используется if/else:

const Student = ({ name }) => <p>Student name: {name}</p>const Teacher = ({ name }) => <p>Teacher name: {name}</p>const Guardian = ({ name }) => <p>Guardian name: {name}</p>export default function SampleComponent({ user }) {let Component = Student;if (user.type === 'teacher') {Component = Teacher} else if (user.type === 'guardian') {Component = Guardian}return (<div><Component name={user.name} /></div>)}

А вот пример использования объекта, хранящего соответствующие значения:

import React from 'react'const Student = ({ name }) => <p>Student name: {name}</p>const Teacher = ({ name }) => <p>Teacher name: {name}</p>const Guardian = ({ name }) => <p>Guardian name: {name}</p>const COMPONENT_MAP = {student: Student,teacher: Teacher,guardian: Guardian}export default function SampleComponent({ user }) {const Component = COMPONENT_MAP[user.type]return (<div><Component name={user.name} /></div>)}

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

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


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

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

import ConfirmationDialog from 'components/global/ConfirmationDialog';export default function useConfirmationDialog({headerText,bodyText,confirmationButtonText,onConfirmClick,}) {const [isOpen, setIsOpen] = useState(false);const onOpen = () => {setIsOpen(true);};const Dialog = useCallback(() => (<ConfirmationDialogheaderText={headerText}bodyText={bodyText}isOpen={isOpen}onConfirmClick={onConfirmClick}onCancelClick={() => setIsOpen(false)}confirmationButtonText={confirmationButtonText}/>),[isOpen]);return {Dialog,onOpen,};}

Пользоваться этим хуком можно так:

import React from "react";import { useConfirmationDialog } from './useConfirmationDialog'function Client() {const { Dialog, onOpen } = useConfirmationDialog({headerText: "Delete this record?",bodyText:"Are you sure you want to delete this record? This cannot be undone.",confirmationButtonText: "Delete",onConfirmClick: handleDeleteConfirm,});function handleDeleteConfirm() {//TODO: удалить}const handleDeleteClick = () => {onOpen();};return (<div><Dialog /><button onClick={handleDeleteClick} /></div>);}export default Client;

Такой подход к абстрагированию компонентов избавляет программиста от необходимости написания больших объёмов шаблонного кода для управления состоянием приложения. Если вы хотите узнать о нескольких полезных хуках React взгляните на этот мой материал.

Разделяйте код компонентов на части


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

Использование обёрток


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

Вот пример компонента, реализующего drag-and-drop с использованием react-beautiful-dnd:

import React from 'react'import { DragDropContext, Droppable } from 'react-beautiful-dnd';export default function DraggableSample() {function handleDragStart(result) {console.log({ result });}function handleDragUpdate({ destination })console.log({ destination });}const handleDragEnd = ({ source, destination }) => {console.log({ source, destination });};return (<div><DragDropContextonDragEnd={handleDragEnd}onDragStart={handleDragStart}onDragUpdate={handleDragUpdate}><Droppable droppableId="droppable" direction="horizontal">{(provided) => (<div {...provided.droppableProps} ref={provided.innerRef}>{columns.map((column, index) => {return (<ColumnComponentkey={index}column={column}/>);})}</div>)}</Droppable></DragDropContext></div>)}

А теперь взгляните на тот же компонент после того, как мы переместили drag-and-drop-логику в компонент-обёртку:

import React from 'react'export default function DraggableSample() {return (<div><DragWrapper>{columns.map((column, index) => {return (<ColumnComponentkey={index}column={column}/>);})}</DragWrapper></div>)}

Вот код обёртки:

import React from 'react'import { DragDropContext, Droppable } from 'react-beautiful-dnd';export default function DragWrapper({children}) {function handleDragStart(result) {console.log({ result });}function handleDragUpdate({ destination }) {console.log({ destination });}const handleDragEnd = ({ source, destination }) => {console.log({ source, destination });};return (<DragDropContextonDragEnd={handleDragEnd}onDragStart={handleDragStart}onDragUpdate={handleDragUpdate}><Droppable droppableId="droppable" direction="horizontal">{(provided) => (<div {...provided.droppableProps} ref={provided.innerRef}>{children}</div>)}</Droppable></DragDropContext>)}

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

Разделение обязанностей


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

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

Паттерн хук появился именно благодаря идее разделения обязанностей. Можно и даже нужно оборачивать в хуки всю логику, ответственную за взаимодействие с некими API или с глобальным состоянием.

Например давайте посмотрим на следующий компонент:

import React from 'react'import { someAPICall } from './API'import ItemDisplay from './ItemDisplay'export default function SampleComponent() {const [data, setData] = useState([])useEffect(() => {someAPICall().then((result) => {setData(result)})}, [])function handleDelete() {console.log('Delete!');}function handleAdd() {console.log('Add!');}const handleEdit = () => {console.log('Edit!');};return (<div><div>{data.map(item => <ItemDisplay item={item} />)}</div><div><button onClick={handleDelete} /><button onClick={handleAdd} /><button onClick={handleEdit} /></div></div>)}

Ниже показано то, что получилось после его рефакторинга, в ходе которого его код разделён на части с применением пользовательских хуков. А именно вот сам компонент:

import React from 'react'import ItemDisplay from './ItemDisplay'export default function SampleComponent() {const { data, handleDelete, handleEdit, handleAdd } = useCustomHook()return (<div><div>{data.map(item => <ItemDisplay item={item} />)}</div><div><button onClick={handleDelete} /><button onClick={handleAdd} /><button onClick={handleEdit} /></div></div>)}

Вот код хука:

import { someAPICall } from './API'export const useCustomHook = () => {const [data, setData] = useState([])useEffect(() => {someAPICall().then((result) => {setData(result)})}, [])function handleDelete() {console.log('Delete!');}function handleAdd() {console.log('Add!');}const handleEdit = () => {console.log('Edit!');};return { handleEdit, handleAdd, handleDelete, data }}

Хранение кода каждого компонента в отдельном файле


Часто программисты пишут код компонентов примерно так:

import React from 'react'export default function SampleComponent({ data }) {export const ItemDisplay = ({ name, date }) => (<div><h3><font color="#3AC1EF">{name}</font></h3><p>{date}</p></div>)return (<div><div>{data.map(item => <ItemDisplay item={item} />)}</div></div>)}

И, хотя тут нет ничего ужасно неправильного, нельзя рекомендовать писать код именно так. У перемещения ItemDisplay в отдельный файл нет никаких минусов. При этом плюсом такого подхода можно назвать тот факт, что, после переноса кода компонента в отдельный файл, компоненты проекта оказываются слабее связаны друг с другом. Их, кроме того, легче будет расширять.

Итоги


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

Что вы посоветовали бы тем, кто хочет сделать код своих React-компонентов чище?

Источник: habr.com
К списку статей
Опубликовано: 20.04.2021 12:16:22
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании ruvds.com

Разработка веб-сайтов

Javascript

Совершенный код

Reactjs

React

Разработка

Ruvds_перевод

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru