Я трачу много времени на разработку новых многоразовых компонентов пользовательского интерфейса.
Это означает, что во время разработки я стараюсь извлекать компоненты пользовательского интерфейса, которые помещаются в общую библиотеку или руководство по стилю.
Эти компоненты используются повторно на протяжении всего проекта. Часто имеют разные стили или макеты при интеграции в функции.
Попутно я выучил несколько правил, которые помогли мне сэкономить время и боль.
1. Держите макет гибким
Я не могу сосчитать, сколько раз компонент имел фиксированную ширину и это влияло на адаптивный дизайн. Это означает, что мне нужно заняться существующими реквизитами, которые были настроены для другой функции где-то на сайте. Нехорошо.
Сохраняя компонент 100% width
, вы передаете ответственность за макет родительскому компоненту.
Например, намного проще использовать CardOne
вместо CardTwo
. Поскольку CardOne
будет реагировать на изменения родительского столбца при изменении размера экрана.
const CardOne = ({ title }) => (
<div style={{ padding: "20px 30px", background: "white" }}>{title}</div>
);
const CardTwo = ({ title }) => (
<div style={{ width: 600, padding: "10px", background: "white" }}>
{title}
</div>
);
const App = () => (
<div className="row">
<div className="col-md-6">
<CardOne title="I'm Fluid" />
</div>
<div className="col-md-6">
<CardTwo title="I will break this layout" />
</div>
</div>
);
2. Разрешить проносить лишний реквизит.
Оказывается, вашему компоненту Button нужен определенный атрибут данных для работы с библиотекой, которую вы используете для одной из конкретных функций. Жаль, что компонент Button
разрешает только className
, children
и onClick
.
Как мы можем это исправить?
Современный ES6 JavaScript позволяет распространять параметры функций и объекты. Его можно использовать для подачи опор к компонентам.
// Before -> <button className="button">Click me</button>
const Button = ({ children, className, onClick }) => (
<button onClick={onClick} className={`${styles.button} ${className || ""}`}>
{children}
</button>
);
// After -> <button className="button" data-theme="dark">Click me</button>
const Button = ({ className, ...props }) => (
<button className={`${styles.button} ${className || ""}`} {...props} />
);
// Example
const App = ({ onActivate }) => (
<SpecificFeature>
<Button data-theme="dark" onClick={onActivate}>
Click me
</Button>
</SpecificFeature>
);
3. Поднимите логику вверх / оставьте ее глупой.
Одна из главных вещей, препятствующих повторному использованию, - это когда логика с отслеживанием состояния, специфичная для функции, реализуется в повторно используемом компоненте.
Представьте, что у вас есть Dropdown
компонент. То, что вы использовали в своей навигации при нажатии на ссылку.
const DropdownItem = ({ className, ...props }) => ( <li className={`dropdown-item ${className || ""}`} {...props} /> );
class Dropdown extends React.Component { state = { toggled: false };
toggle = () => this.setState(state => ({ toggled: !state.toggled }));
render() { return ( <div className="navbar-link"> <span onClick={this.toggle}> {this.props.title} </span> {this.state.toggled && ( <ul className="dropdown"> {this.props.children} </ul> )} </div> ); } }
const Navbar = () => ( <div className="navbar"> <div className="navbar-brand">Example</div> <ul className="navbar-menu"> <Dropdown title="My Account"> <DropdownItem>One</DropdownItem> <DropdownItem>Two</DropdownItem> </Dropdown> </ul> </div> );
Проблема выше:
Dropdown
обрабатывает логику открытия и закрытия раскрывающегося списка. Значительно затрудняет повторное использованиеDropdown
для более сложных сценариев (например: открывать раскрывающийся список только при успешном запросе).Dropdown
имеет разметку, относящуюся к компонентуNavbar
.
Если мы реорганизуем компонент Dropdown
, чтобы повысить логику. В результате мы получаем гораздо более Dropdown
компонент многократного использования.
const DropdownItem = ({ className, ...props }) => ( <li className={`dropdown-item ${className || ""}`} {...props} /> );
const Dropdown = ({ className, ...props }) => ( <ul className={`dropdown ${className || ""}`} {...props} /> );
class Navbar extends React.Component { state = { showUserOptions: false };
toggleOptions = () => this.setState(state => ({ showUserOptions: !state.showUserOptions }));
render() { return ( <div className="navbar"> <div className="navbar-brand">Example</div> <ul className="navbar-menu"> <div className="navbar-link"> <span onClick={this.toggleOptions}>My Account</span> {this.state.showUserOptions && ( <Dropdown> <DropdownItem>One</DropdownItem> <DropdownItem>Two</DropdownItem> </Dropdown> )} </div> </ul> </div> ); } }
Резюме
- Сохраняйте гибкость компоновки компонентов.
- Позволяет передавать в компонент дополнительные свойства.
- По возможности держите их подальше от логики.