
Published on :Dec 09, 2019
Патерни проєктування в React
1. Вступ
Після прочитання "Head First Design Patterns", стає очевидним, що багато хто з цих патернів не тільки актуальні в сучасній розробці, а й активно застосовуються в таких популярних бібліотеках, як React. У цій статті ми розглянемо, як основні патерни проектування застосовуються в React і React Native, а також наведемо практичні приклади коду.
React - це бібліотека JavaScript для створення користувацьких інтерфейсів. Вона дає змогу розробникам створювати великі веб-додатки, які можуть оновлюватися без перезавантаження сторінки, роблячи їх швидкими та чуйними. З іншого боку, React Native дає змогу використовувати ту саму філософію та підходи, але для створення мобільних додатків.
2. Компонентний підхід у React і патерн "Композит"
Суть компонентного підходу:
У серці React лежить ідея поділу інтерфейсу на безліч маленьких, незалежних компонентів, що використовуються повторно. Ці компоненти можуть бути вкладені один в одного, створюючи складні, але керовані структури.
Приклад коду:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Alice" />
<Welcome name="Bob" />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
У наведеному прикладі в нас є два компоненти: Welcome і App. Компонент App включає в себе два екземпляри компонента Welcome.
Порівняння з патерном "Композит":
Паттерн "Композит" з книги "Head First Design Patterns" описує об'єкти, згруповані в деревоподібні структури, щоб представляти ієрархії цілого/частини. Так само в React компоненти можна сприймати як дерево, де кожен компонент може містити інші компоненти.
3. Virtual DOM і патерн "Проксі"
Що таке Virtual DOM і які його переваги:
Virtual DOM - це легковажне представлення реального DOM у пам'яті. Основна ідея Virtual DOM - це зробити високорівневе оновлення (diffing) і, потім, ефективно застосувати мінімальні зміни до реального DOM.
Переваги Virtual DOM:
- Ефективність: Замість оновлення всього DOM-дерева React оновлює тільки ті частини, які були змінені.
- Продуктивність: Робота з реальним DOM повільна через перерахунки та перемальовування. Virtual DOM дає змогу мінімізувати звернення до реального DOM.
- Абстракція: Virtual DOM надає зручний спосіб програмування без необхідності прямої роботи з реальним DOM.
Приклад коду:
// Описуємо зміну у Virtual DOM
const virtualNode = {
type: 'h1',
props: { title: 'Hello, World!' },
};
// React оновлює реальний DOM на основі відмінностей із попереднім Virtual DOM
ReactDOM.render(virtualNode, document.getElementById('root'));
Порівняння з патерном "Проксі":
Паттерн "Проксі" являє собою об'єкт, який контролює доступ до іншого об'єкта. Virtual DOM діє як проксі для реального DOM. Замість прямої взаємодії з реальним DOM, розробники взаємодіють з Virtual DOM. Коли зміни готові до застосування, React оптимізує і застосовує їх до реального DOM. Таким чином, Virtual DOM служить проксі, мінімізуючи й оптимізуючи взаємодію з реальним DOM.
4. State і Props у React і патерн "Стратегія"
Особливості State і Props:
State: Це дані, які можуть змінюватися в процесі життєвого циклу компонента. State використовується для відстеження змін у компоненті та перемальовування його за необхідності.
Props: Це аргументи, що передаються в компонент із батьківського компонента. Вони дозволяють передати дані або зворотні виклики дочірнім компонентам.
Приклад коду:
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = { clicked: false };
}
handleClick = () => {
this.setState({ clicked: true });
};
render() {
return (
<div>
<h1>Hello, {this.props.name}</h1>
{this.state.clicked ? (
<p>Clicked!</p>
) : (
<button onClick={this.handleClick}>Click me</button>
)}
</div>
);
}
}
// Використання компонента
<Welcome name="Alice" />;
Порівняння з патерном "Стратегія":
Паттерн "Стратегія" визначає сімейство алгоритмів, інкапсулює кожен із них і забезпечує їхню взаємозамінність. У контексті React, props можуть бути розглянуті як стратегія, яку батьківський компонент передає дочірньому. Дочірній компонент може змінювати свою поведінку залежно від отриманих props, не змінюючи свою внутрішню логіку.
5. Higher-Order Components (HOC) і патерн "Декоратор"
Як працює HOC:
Higher-Order Component (HOC) - це функція, яка приймає компонент і повертає новий компонент із доданим або зміненим функціоналом. Здебільшого, HOC використовуються для повторного використання логіки компонента, не втручаючись у його внутрішні деталі.
Приклад коду:
function withLogging(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} has mounted`);
}
componentWillUnmount() {
console.log(`Component ${WrappedComponent.name} will unmount`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
class MyComponent extends React.Component {
render() {
return <div>Hello from MyComponent</div>;
}
}
const MyComponentWithLogging = withLogging(MyComponent);
У наведеному прикладі HOC withLogging додає логування в момент монтування та розмонтування компонента.
Порівняння з патерном "Декоратор":
Паттерн "Декоратор" надає спосіб динамічного додавання обов'язків об'єктам. HOC дуже схожий на патерн "Декоратор": він "обертає" компонент і додає або модифікує його функціонал без необхідності змінювати оригінальний компонент.
У прикладі вище, withLogging декорує MyComponent, додаючи логування, але при цьому не змінюючи сам MyComponent.
6. Hooks і патерн "Команда"
Пояснення принципу роботи Hooks:
Hooks - це нововведення в React, що дає змогу використовувати стан та інші можливості React без написання класів. За допомогою хуків можна "підключати" додаткову логіку до функціональних компонентів.
Приклад коду:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
У цьому прикладі використовується хук useState для додавання стану у функціональний компонент.
Порівняння з патерном "Команда":
Патерн "Команда" інкапсулює запит в об'єкті, даючи змогу параметризувати клієнтів різними запитами, ставлячи чергу або логуючи запити, а також підтримуючи скасовані операції. У React, кожен хук може розглядатися як "команда", яка додає певний функціонал або поведінку до компонента. У прикладі вище, команда useState надає функціонал стану.
7. Context API і патерн "Одинак"
Як працює Context API:
Context API надає спосіб передачі даних через дерево компонентів без необхідності передавати props вручну на кожному рівні. Це може бути корисно для таких даних, як налаштування локалізації, теми або авторизований користувач.
Приклад коду:
import React, { createContext, useContext } from 'react';
// Створення контексту
const ThemeContext = createContext('light');
function ThemedButton() {
// Використання контексту
const theme = useContext(ThemeContext);
return <button theme={theme}>Click me</button>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}
У цьому прикладі створюється ThemeContext за допомогою createContext(). Потім контекст використовується в компоненті ThemedButton за допомогою хука useContext.
Порівняння з патерном "Одинак":
Паттерн "Одинак" гарантує, що клас має тільки один екземпляр, і надає глобальну точку доступу до цього екземпляра. Context API в React діє схожим чином, надаючи глобальну точку доступу до даних для всіх компонентів у дереві, без необхідності передачі даних явно через props.
У нашому прикладі, ThemeContext може розглядатися як "Одинак", оскільки він надає єдине джерело істини для теми в дереві компонентів.
8. React Native та адаптація патернів проектування для мобільних додатків
Особливості React Native:
React Native - це фреймворк для створення мобільних застосунків із використанням тих самих принципів і підходів, що й у React. Основна відмінність полягає в тому, що React Native використовує нативні компоненти для відображення інтерфейсу, а не DOM-елементи.
- Нативні компоненти: Замість HTML-тегів React Native використовує нативні компоненти, такі як
<View>,<Text>і<Image>. - Міст між JavaScript і нативним кодом: React Native використовує спеціальний "міст", щоб JavaScript-код міг взаємодіяти з нативними модулями та компонентами.
- Модульність: Так само як і в React, у React Native ви створюєте модульні та повторно використовувані компоненти.
Приклад коду:
import React from 'react';
import { View, Text, Button } from 'react-native';
function App() {
return (
<View>
<Text>Hello, React Native!</Text>
<Button title="Click me" onPress={() => alert('Button clicked!')} />
</View>
);
}
Застосування патернів у мобільній розробці:
Багато з патернів проектування, розглянутих раніше, також застосовні і в React Native:
- Композит: Так само як і у веб-версії React, ви будуєте додатки з безлічі вкладених компонентів.
- Проксі: У деяких випадках, коли ви хочете оптимізувати взаємодію між JavaScript і нативним кодом, ви можете використовувати патерн "Проксі" для мінімізації кількості переданих даних.
- Стратегія: Ви можете використовувати різні стратегії для визначення поведінки компонентів залежно від платформи (iOS або Android).
- Декоратор і HOC: Так само як і в React, ви можете використовувати вищі порядкові компоненти для додавання додаткової функціональності.
- Команда: За допомогою хуків і колбеків ви визначаєте дії, які будуть виконані у відповідь на різні події.
- Одинак: Контексти та глобальні стани можуть бути реалізовані з використанням патерну "Одинак".
9. Висновок
Після глибокого занурення у світ React і React Native стає очевидно, що сучасні фронтенд-технології не є чимось абсолютно новим або відірваним від класичного програмування. Насправді, вони активно використовують патерни проєктування, які були виведені й описані десятиліттями раніше.
React пропонує новий спосіб мислення про створення користувацьких інтерфейсів, але в основі його лежать ті самі перевірені часом принципи і патерни, які програмісти використовують протягом багатьох років. Це не тільки робить React і React Native привабливими для досвідчених розробників, а й допомагає новачкам швидше освоїтися, якщо вони вже знайомі з класичними патернами проектування.
React Native, незважаючи на свою унікальність в аспекті мобільного розроблення, також активно використовує ті самі патерни, що і його "брат" React, надаючи однаковий підхід до розроблення як веб, так і мобільних додатків.
Насамкінець можна сказати, що вивчення патернів проектування, таких як ті, що представлені в "Head First Design Patterns", безумовно, варте того. Вони не тільки допоможуть вам стати кращими в програмуванні класичними мовами, а й полегшать розробку на сучасних платформах і фреймворках, таких як React і React Native.
10. Література та посилання
- Freeman, E., & Robson, E. (2004). Head First Design Patterns. O'Reilly Media.
- Класичний вступ до патернів проектування зі зрозумілими прикладами та ілюстраціями.
- React - основна документація
- Офіційна документація React
- Повний посібник з React, його компонентів, станів, пропсів та інших основних концепцій.
- React Native - основна документація
- Офіційна документація React Native
- Ресурс для тих, хто хоче почати розробку мобільних додатків з використанням React Native.
- React Patterns
- reactpatterns.com
- Добірка популярних патернів проектування, що використовуються в React.
- Context API
- Вступ до Context API
- Посібник з використання Context API в React для управління глобальними станами.
- Hooks в React
- Офіційний посібник з Hooks
- Розуміння та застосування хуків у React для поліпшення структури коду та керування станом компонента.
- Design Patterns: Elements of Reusable Object-Oriented Software Gamma, E., Helm, R., Johnson, R., & Vlissides, J.
- Ще одна класична книжка з паттернів проектування, яка дає глибоке розуміння теми.
Цей список дає базове розуміння і ресурси для подальшого вивчення патернів проєктування в контексті React і програмування загалом. Завжди корисно тримати під рукою надійні ресурси для довідки та навчання.