От создателя: в этом посте давайте разглядим несколько обычных советов, которые посодействуют для вас писать наиболее незапятнанные составляющие React и лучше масштабировать проект.
Опасайтесь передачи параметров c помощью оператора разделения
Давайте поначалу начнем с антипаттерна, которого для вас следует избегать. Если нет определенной и обоснованной предпосылки, чтоб создать это, вы должны избегать прохождения параметров вниз дерева компонентов, используя оператор разделения, к примеру, так: {…props}.
Таковая передача параметров вправду ускоряет написание компонентов. Но она также затрудняет выявление ошибок в коде. Вы теряете уверенность в собственных компонентах, что затрудняет их рефакторинг, и в итоге ошибки начнут появляться намного ранее.
Оберните характеристики функций в объект
Если ваша функция воспринимает несколько характеристик, рекомендуется заключить их в объект. Вот вам наглядный пример:
JavaScript export const sampleFunction = ({ param1, param2, param3 }) => { console.log({ param1, param2, param3 });
}
123 | export const sampleFunction = ({ param1, param2, param3 }) => { console.log({ param1, param2, param3 });} |
Таковой метод написания функции дает несколько приметных преимуществ:
Для вас больше не надо волноваться о порядке передачи аргументов. Я делал такую ошибку пару раз, когда передавал аргументы функции в неверном порядке.
Для редакторов с настроенным IntelliSense вы получите автозаполнение для аргументов функции.
Для обработчиков событий используйте функции, возвращающие функции обработчика
Если вы знакомы с многофункциональным программированием, этот способ припоминает каррирование, так как вы заблаговременно устанавливаете некие характеристики. Давайте разглядим пример:
JavaScript export default function SampleComponent({ onValueChange }) { const handleChange = (key) => { return (e) => onValueChange(key, e.target.value)
}
return ( )
}
12345678910111213 | export default function SampleComponent({ onValueChange }) { const handleChange = (key) => { return (e) => onValueChange(key, e.target.value) } return ( )} |
Видите ли, написав таковым образом функции-обработчики, вы сможете сохранить дерево компонентов наиболее незапятнанным.
Использйте map заместо if / else
Когда для вас необходимо показать разные элементы на базе пользовательской логики, я предлагаю применять map заместо операторов if / else. Вот вам наглядный пример использования if / else:
JavaScript const Student = ({ name }) =>
Student name: {name}
const Teacher = ({ name }) =>
Teacher name: {name}
const Guardian = ({ name }) =>
Guardian name: {name}
export default function SampleComponent({ user }) { let Component = Student; if (user.type === ‘teacher’) { Component = Teacher } else if (user.type === ‘guardian’) { Component = Guardian
}
return ( )
}
12345678910111213141516171819 | const Student = ({ name }) =>
Student name: {name} const Teacher = ({ name }) => Teacher name: {name} const Guardian = ({ name }) => Guardian name: {name} export default function SampleComponent({ user }) { let Component = Student; if (user.type === ‘teacher’) { Component = Teacher } else if (user.type === ‘guardian’) { Component = Guardian } return ( )} |
Как легко создавать текстовые градиенты с помощью CSS
А вот вам наглядный пример использования map:
JavaScript import React from ‘react’
const Student = ({ name }) =>
Student name: {name}
const Teacher = ({ name }) =>
Teacher name: {name}
const Guardian = ({ name }) =>
Guardian name: {name}
const COMPONENT_MAP = { student: Student, teacher: Teacher, guardian: Guardian
}
export default function SampleComponent({ user }) {
const Component = COMPONENT_MAP[user.type]
return ( )
}
123456789101112131415161718192021 | import React from ‘react’ const Student = ({ name }) =>
Student name: {name} const Teacher = ({ name }) => Teacher name: {name} const Guardian = ({ name }) => Guardian name: {name} const COMPONENT_MAP = { student: Student, teacher: Teacher, guardian: Guardian} export default function SampleComponent({ user }) { const Component = COMPONENT_MAP[user.type] return ( )} |
Используя эту ординарную стратегию, ваши составляющие станут наиболее декларативными и наиболее ординарными для осознания. Это также упрощает расширение логики и добавление в нее доп частей.
Хуки компонентов
Считаю этот паттерн полезным, если вы не злоупотребляете им. Вы сможете найти, что используете некие составляющие во всем собственном приложении. Если им для работы требуется состояние, вы сможете обернуть их хуком, обеспечивающим это состояние. Неплохими примерами таковых компонентов являются всплывающие окна, всплывающие извещения либо обыкновенные модальные окна. К примеру, вот хук компонента для обычного модального окна доказательства:
JavaScript 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( () => ( setIsOpen(false)} confirmationButtonText={confirmationButtonText} /> ), [isOpen]
);
return { Dialog, onOpen,
};
}
12345678910111213141516171819202122232425262728293031323334353637 | 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( () => ( setIsOpen(false)} confirmationButtonText={confirmationButtonText} /> ), [isOpen] ); return { Dialog, onOpen, }; } |
Потом вы сможете применять собственный компонентный хук так:
JavaScript import React from «react»;
import { useConfirmationDialog } from ‘./useConfirmationDialog’
function Client() { const { Dialog, onOpen } = useConfirmationDialog({ headerText: «Delete this record?», bodyText: «Are you sure you want delete this record? This cannot be undone.», confirmationButtonText: «Delete», onConfirmClick: handleDeleteConfirm,
});
function handleDeleteConfirm() { //TODO: delete
}
const handleDeleteClick = () => { onOpen();
};
return ( );
}
export default Client;
123456789101112131415161718192021222324252627282930 | import React from «react»;import { useConfirmationDialog } from ‘./useConfirmationDialog’ function Client() { const { Dialog, onOpen } = useConfirmationDialog({ headerText: «Delete this record?», bodyText: «Are you sure you want delete this record? This cannot be undone.», confirmationButtonText: «Delete», onConfirmClick: handleDeleteConfirm, }); function handleDeleteConfirm() { //TODO: delete } const handleDeleteClick = () => { onOpen(); }; return ( );} export default Client; |
Такое абстрагирование компонента устраняет вас от написания огромного количества шаблонного кода управления состоянием.
Разделение компонентов
Последующие три совета относятся к разумному разделению компонентов. По моему опыту, сохранение маленьких размеров компонентов — наилучший метод сохранить маневренность проекта.
Российский рынок интернет-рекламы вырос на 14%
Используйте обертки
Если вы пытаетесь отыскать метод поделить собственный большенный компонент, поглядите на функциональность, которую предоставляет любой элемент вашего компонента. Некие элементы предусмотрены для обеспечения особенной функциональности, к примеру, обработчики перетаскивания.
Вот вам наглядный пример компонента, который реализует перетаскивание при помощи response-beautiful-dnd:
JavaScript import React from ‘react’
import { DragDropContext, Droppable } from ‘react-beautiful-dnd’;
export default function DraggableSample() { function handleDragStart(result) { console.log({ result });
}
%MINIFYHTML437c09dde7c3989053d823ef87c9b4d214% %MINIFYHTML437c09dde7c3989053d823ef87c9b4d215%
function handleDragUpdate({ destination }) console.log({ destination });
}
const handleDragEnd = ({ source, destination }) => { console.log({ source, destination });
};
return ( {(provided) => ( {columns.map((column, index) => { return ( ); })} )} )
}
123456789101112131415161718192021222324252627282930313233343536373839404142 | 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 ( {(provided) => ( {columns.map((column, index) => { return ( ); })} )} )} |
Сейчас проверьте компонент опосля того, как мы переместили всю логику перетаскивания в компонент-оболочку:
JavaScript import React from ‘react’
export default function DraggableSample() { return ( {columns.map((column, index) => { return ( ); })} )
}
123456789101112131415161718 | import React from ‘react’ export default function DraggableSample() { return ( {columns.map((column, index) => { return ( ); })} )} |
А вот код оболочки:
JavaScript 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 ( {(provided) => ( {children} )} )
}
1234567891011121314151617181920212223242526272829303132 | 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 ( {(provided) => ( {children} )} )} |
В итоге легче посмотреть на компонент и осознать, что он делает на высочайшем уровне. Вся функциональность перетаскивания находится в оболочке, и её легче осознать.
Разделение задач
Это мой возлюбленный способ разделения огромных компонентов. В контексте React разделение задач значит разделение частей компонентов, отвечающих за подборку и изменение данных, и частей, отвечающих только за отображение дерева частей.
Этот способ разделения задач является главный предпосылкой возникновения шаблона хуков. Вы сможете и должны обернуть всю логику, которая управляет API-интерфейсами либо подключениями к глобальному состоянию, при помощи пользовательского хука.
К примеру, разглядим компонент:
JavaScript 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 ( {data.map(item => )} )
}
123456789101112131415161718192021222324252627282930313233343536373839 | 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 ( {data.map(item => )} )} |
Сейчас вот его реорганизованная версия с разделением кода при помощи пользовательских хуков:
JavaScript import React from ‘react’
import ItemDisplay from ‘./ItemDisplay’
export default function SampleComponent() {
const { data, handleDelete, handleEdit, handleAdd } = useCustomHook()
return ( {data.map(item => )} )
}
12345678910111213141516171819 | import React from ‘react’import ItemDisplay from ‘./ItemDisplay’ export default function SampleComponent() { const { data, handleDelete, handleEdit, handleAdd } = useCustomHook() return ( {data.map(item => )} )} |
А вот и сам хук:
JavaScript 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 }
}
1234567891011121314151617181920212223242526 | 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 }} |
Отдельный файл для всякого компонента
Нередко люди пишут таковой код:
JavaScript import React from ‘react’
export default function SampleComponent({ data }) {
export const ItemDisplay = ({ name, date }) => (
{name}
{date}
)
return ( {data.map(item => )} )
}
1234567891011121314151617181920 | import React from ‘react’ export default function SampleComponent({ data }) { export const ItemDisplay = ({ name, date }) => (
{name}
{date} ) return ( {data.map(item => )} )} |
Хотя в написании такового кода нет ничего отвратительного, следовать ему не рекомендуется. У перевода ItemDisplay в отдельный файл нет недочетов, а плюсы состоят в том, что ваши составляющие слабо соединены и их легче расширять.
Написание незапятнанного кода по большей части сводится к тому, чтоб быть внимательным и отыскивать время, чтоб следовать советам и избегать антипаттернов. Так что если вы потратите время на то, чтоб следовать сиим шаблонам, это поможет для вас писать наиболее незапятнанные составляющие React. Я считаю эти шаблоны весьма полезными в моем проекте, и надеюсь, что вы тоже!
Создатель: Iskander Samatov
Редакция: Команда webformyself.
Читайте нас в Telegram, VK, Yandex.Дзен
Источник
Вам также может понравиться