Давай встретимся в телеграмме:- https://t.me/nodereact
Оглавление
- Возврат функций из обработчиков
- Раздельные обязанности
- Использовать карту объектов вместо условий
- Поместите независимые переменные за пределы жизненного цикла React
Функции возврата из обработчиков
Если вы знакомы с функциональным программированием, то понимаете, о чем я говорю. Мы можем легко назвать это каррированием. По сути, мы заранее задаем некоторые параметры функции.
У нас есть явная проблема с шаблонным кодом ниже. Эта техника поможет нам!
export default function App() { const [user, setUser] = useState({ name: "", surname: "", address: "" }); // First handler const handleNameChange = (e) => { setUser((prev) => ({ ...prev, name: e.target.value })); }; // Second handler! const handleSurnameChange = (e) => { setUser((prev) => ({ ...prev, surname: e.target.value })); }; // Third handler!!! const handleAddressChange = (e) => { setUser((prev) => ({ ...prev, address: e.target.value })); }; // What if we need one more input? Should we create another handler for it? return ( <> <input value={user.name} onChange={handleNameChange} /> <input value={user.surname} onChange={handleSurnameChange} /> <input value={user.address} onChange={handleAddressChange} /> </> ); }
Решение
export default function App() { const [user, setUser] = useState({ name: "", surname: "", address: "" }); const handleInputChange = (field) => { return (e) => { setUser((prev) => ({ ...prev, [field]: e.target.value })); }; }; return ( <> <input value={user.name} onChange={handleInputChange("name")} /> <input value={user.surname} onChange={handleInputChange("surname")} /> <input value={user.address} onChange={handleInputChange("address")} /> {JSON.stringify(user)} </> ); }
Отдельные обязанности
Создание компонента «Бог» — распространенная ошибка разработчиков. Он называется «Бог», потому что содержит много строк кода, которые трудно понять и поддерживать. Я настоятельно рекомендую разделить компоненты на наборы независимых подмодулей.
Типичная структура для этого будет:
- модуль пользовательского интерфейса, отвечающий только за визуальное представление.
- Модуль логики/модели, содержащий только бизнес-логику. Примером этого является пользовательский хук.
- Модуль библиотеки, содержащий все необходимые утилиты для компонента.
Вот небольшой демонстрационный пример, который поможет проиллюстрировать эту концепцию.
export function ListComponent() { // Our local state const [list, setList] = useState([]); // Handler to load data from the server const fetchList = async () => { try { const resp = await fetch("https://www.url.com/list"); const data = await resp.json(); setList(data); } catch { showAlert({ text: "Something went wrong!" }); } }; // We want to fetch only on mount useEffect(() => { fetchList(); }, []); // Handler responsible for deleting items const handleDeleteItem = (id) => { return () => { try { fetch(`https://www.url.com/list/${id}`, { method: "DELETE" }); setList((prev) => prev.filter((x) => x.id !== id)); } catch { showAlert({ text: "Something went wrong!" }); } }; }; // Here we just render our data items return ( <div className="list-component"> {list.map(({ id, name }) => ( <div key={id} className="list-component__item>"> {/* We want to trim long name with ellipsis */} {name.slice(0, 30) + (name.length > 30 ? "..." : "")} <div onClick={handleDeleteItem(id)} className="list-component__icon"> <DeleteIcon /> </div> </div> ))} </div> ); }
Мы должны начать с написания наших утилит, которые будут использоваться в модулях модели и пользовательского интерфейса.
export async function getList(onSuccess) { try { const resp = await fetch("https://www.url.com/list"); const data = await resp.json(); onSuccess(data) } catch { showAlert({ text: "Something went wrong!" }); } } export async function deleteListItem(id, onSuccess) { try { fetch(`https://www.url.com/list/${id}`, { method: "DELETE" }); onSuccess() } catch { showAlert({ text: "Something went wrong!" }); } } export function trimName(name) { return name.slice(0, 30) + (name.lenght > 30 ? '...' : '') }
Теперь нам нужно реализовать нашу бизнес-логику. Это просто будет специальный хук, возвращающий то, что нам нужно.
export function useList() { const [list, setList] = useState([]); const handleDeleteItem = useCallback((id) => { return () => { deleteListItem(id, () => { setList((prev) => prev.filter((x) => x.id !== id)); }) }; }, []); useEffect(() => { getList(setList); }, []); return useMemo( () => ({ list, handleDeleteItem }), [list, handleDeleteItem] ); }
Последний шаг — написать наш модуль пользовательского интерфейса, а затем объединить все это вместе.
export function ListComponentItem({ name, onDelete }) { return ( <div className="list-component__item>"> {trimName(name)} <div onClick={onDelete} className="list-component__icon"> <DeleteIcon /> </div> </div> ); } export function ListComponent() { const { list, handleDeleteItem } = useList(); return ( <div className="list-component"> {list.map(({ id, name }) => ( <ListComponentItem key={id} name={name} onDelete={handleDeleteItem(id)} /> ))} </div> ); }
Использовать карту объектов вместо условий
Если вам нужно отображать различные элементы в зависимости от переменной, вы можете реализовать этот совет. Использование этой простой стратегии делает компоненты более декларативными и упрощает понимание кода. Кроме того, это делает более безболезненным дальнейшее расширение функциональности.
Проблема
🌟 Не забудьте подписаться/присоединиться к https://t.me/nodereact, чтобы узнать вещи Mern Stack!
function Account({type}) { let Component = UsualAccount if (type === 'vip') { Component = VipAccount } if (type === 'moderator') { Component = ModeratorAccount } if (type === 'admin') { Component = AdminAccount } return ( <div className='account'> <Component /> <AccountStatistics /> </div> ) }
Решение
const ACCOUNTS_MAP = { 'vip': VipAccount, 'usual': UsualAccount, 'admin': AdminAccount, 'moderator': ModeratorAccount, } function Account({type}) { const Component = ACCOUNTS_MAP[type] return ( <div className='account'> <Component /> <AccountStatistics /> </div> ) }
Поместите независимые переменные за пределы жизненного цикла React
Идея состоит в том, чтобы отделить логику, которая не требует методов жизненного цикла компонента React, от самого компонента. Это улучшает ясность кода, делая зависимости более явными. Поэтому становится намного проще читать и понимать компоненты.
Проблема
function useItemsList() { const defaultItems = [1, 2, 3, 4, 5] const [items, setItems] = useState(defaultItems) const toggleArrayItem = (arr, val) => { return arr.includes(val) ? arr.filter(el => el !== val) : [...arr, val]; } const handleToggleItem = (num) => { return () => { setItems(toggleArrayItem(items, num)) } } return { items, handleToggleItem, } }
Решение
const DEFAULT_ITEMS = [ 1, 2, 3, 4, 5 ] const toggleArrayItem = (arr, val) => { return arr.includes(val) ? arr.filter(el => el !== val) : [...arr, val]; } function useItemsList() { const [items, setItems] = useState(DEFAULT_ITEMS) const handleToggleItem = (num) => { return () => { setItems(toggleArrayItem(items, num)) } } return { items, handleToggleItem, } }
Спасибо, что прочитали!
Надеюсь, вы нашли эту статью полезной. Если у вас есть какие-либо вопросы или предложения, пожалуйста, оставляйте комментарии. Ваши отзывы помогают мне стать лучше.
Не забудь подписаться⭐️
Давайте встретимся в телеграмме: - https://t.me/nodereact