Існує кілька способів змусити компоненти спілкуватися. Деякі з них можуть підходити до вашої корисної коробки. Ось перелік деяких, які я вважаю корисними знати.
Реагуйте
Пряме спілкування батьків / дитини
const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);
class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}
Тут дочірній компонент викличе зворотний виклик, наданий батьком зі значенням, і батько зможе отримати значення, надане дітьми у батьків.
Якщо ви створюєте функцію / сторінку свого додатка, краще мати єдиного батька, який керує зворотними зворотами / станом (також називаються container
або smart component
), а всі діти мають статус без громадянства, лише повідомляючи про це батькові. Таким чином ви можете легко «поділитися» станом батька будь-якій дитині, яка цього потребує.
Контекст
React Context дозволяє утримувати стан у корені вашої ієрархії компонентів і мати можливість легко вводити цей стан у дуже глибоко вкладені компоненти, без клопоту не потрібно передавати реквізити до кожного проміжного компонента.
Досі контекст був експериментальною особливістю, але новий API доступний у React 16.3.
const AppContext = React.createContext(null)
class App extends React.Component {
render() {
return (
<AppContext.Provider value={{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};
const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);
Споживач використовує схему функцій рендерінга / дітей
Перегляньте цю публікацію в блозі для отримання більш детальної інформації.
Перед React 16.3 я рекомендую використовувати реакцію-трансляцію, яка пропонує досить подібний API, і використовувати колишній контекстний API.
Портали
Використовуйте портал, коли ви хочете тримати близько двох компонентів, щоб вони спілкувалися з простими функціями, як, наприклад, у звичайного батька / дитини, але ви не хочете, щоб ці 2 компоненти мали стосунки батько / дитина в DOM, оскільки візуальних / CSS обмежень, які він має на увазі (наприклад, z-індекс, непрозорість ...).
У цьому випадку ви можете використовувати «портал». Існують різні бібліотеки реагування за допомогою порталів , які зазвичай використовуються для модалів , спливаючих вікон, підказок ...
Розглянемо наступне:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
Можливо, видається такий DOM, коли він відображається всередині reactAppContainer
:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
Детальніше тут
Слоти
Ви десь визначите слот, а потім заповніть слот з іншого місця вашого дерева візуалізації.
import { Slot, Fill } from 'react-slot-fill';
const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>
export default Toolbar;
export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>
Це трохи схоже на портали, за винятком того, що заповнений вміст відображатиметься у визначеному вами слоті, тоді як портали, як правило, створюють новий доменний вузол (часто це діти document.body)
Перевірте бібліотеку реакцій-заповнення слотів
Автобус події
Як зазначено в документації React :
Для спілкування між двома компонентами, які не мають стосунків батько-дитина, ви можете налаштувати власну глобальну систему подій. Підпишіться на події компонента компонентDidMount (), скасуйте підписку на компонент компонент WillUnmount () та зателефонуйте setState () при отриманні події.
Є багато речей, які можна використовувати для налаштування шини подій. Ви можете просто створити масив слухачів, і при публікації події всі слухачі отримають подію. Або ви можете використовувати щось на кшталт EventEmitter або PostalJs
Флюс
Flux - це в основному автобус подій, за винятком того, що приймачі подій - це магазини. Це схоже на базову систему шин подій, за винятком того, що стан керується поза React
Оригінальна реалізація Flux виглядає як спроба зробити пошук подій хакейним способом.
Redux - це для мене реалізація Flux, яка є найближчою до джерел пошуку подій, яка приносить користь багатьом перевагам подій, як, наприклад, можливість подорожувати часом. Він не суворо пов'язаний з React і може також використовуватися з іншими функціональними бібліотеками перегляду.
Redux розумник в відео - підручник дуже приємно , і пояснює , як вона працює всередині (це дійсно просто).
Курсори
Курсори надходять від ClojureScript / Om та широко використовуються в проектах React. Вони дозволяють керувати станом за межами React, і дозволяють безлічі компонентів мати доступ для читання / запису до тієї ж частини штату, не потребуючи нічого знати про дерево компонентів.
Існує багато реалізацій, включаючи ImmutableJS , React-cursors та Omniscient
Редагувати 2016 : Схоже, люди погоджуються, що курсори працюють добре для менших додатків, але це не дуже добре масштабує складні програми. У Om Next вже немає курсорів (хоча спочатку концепція введена в Om,)
В'яз архітектури
Архітектура Elm - це архітектура, запропонована для використання мовою Elm . Навіть якщо Elm не є ReactJS, архітектура Elm може бути виконана і в React.
Дан Абрамов, автор Redux, здійснив реалізацію архітектури Elm, використовуючи React.
І Redux, і Elm дійсно чудові і, як правило, розширюють можливості пошуку подій на передній панелі, що дозволяє налагоджувати час відключення, скасовувати / повторювати, повторювати ...
Основна відмінність Redux від Elm полягає в тому, що Elm, як правило, набагато суворіше щодо управління державою. У Elm у вас не може бути локального стану компонента або гачок кріплення / відключення, і всі зміни DOM повинні бути спровоковані глобальними змінами стану. Архітектура Elm пропонує масштабований підхід, який дозволяє обробляти ВСЕ стан усередині одного незмінного об'єкта, тоді як Redux пропонує підхід, який пропонує вам обробляти MOST стану в одному незмінному об'єкті.
Хоча концептуальна модель Elm дуже елегантна, і архітектура дозволяє добре масштабувати великі програми, на практиці це може бути важко або залучати більше котла для досягнення простих завдань, таких як фокусування на вході після його монтажу або інтеграція з існуючою бібліотекою з імперативним інтерфейсом (тобто плагін JQuery). Супутнє питання .
Крім того, архітектура Elm включає більше кодових плит. Писати це не так багатослівно чи складно, але я думаю, що архітектура Elm більше підходить для мов, які набираються статично.
FRP
Бібліотеки, такі як RxJS, BaconJS або Kefir, можуть використовуватися для створення потоків FRP для обробки зв'язку між компонентами.
Ви можете спробувати, наприклад, Rx-React
Я думаю, що використовувати ці вкладки досить схоже на те, що пропонує мова ELM із сигналами .
CycleJS не використовує ReactJS, але використовує vdom . Він поділяє багато подібності з архітектурою Elm (але простіший у використанні в реальному житті, оскільки він дозволяє гачки vdom), і він широко використовує RxJs замість функцій, і може стати хорошим джерелом натхнення, якщо ви хочете використовувати FRP з Реагуйте. Відео CycleJs Egghead приємно зрозуміти, як це працює.
CSP
CSP (Комунікація послідовних процесів) в даний час популярний (в основному через Go / goututines і core.async / ClojureScript), але ви можете використовувати їх також у javascript з JS-CSP .
Джеймс Лонг зробив відео, де пояснив, як це можна використовувати з React.
Саги
Сага - це бекенд-концепція, що походить із світу DDD / EventSourcing / CQRS, також називається "менеджер процесів". Він популяризується проектом redux-saga , здебільшого як заміна на redux-thunk для обробки побічних ефектів (тобто дзвінків API тощо). Більшість людей наразі вважають, що це лише сервіси для побічних ефектів, але насправді йдеться про роз’єднання компонентів.
Це скоріше комплімент архітектурі Flux (або Redux), ніж абсолютно нова система зв'язку, тому що сага випромінює дії Flux наприкінці. Ідея полягає в тому, що якщо у вас є widget1 і widget2, і ви хочете, щоб вони були роз'єднані, ви не можете запускати націлювання на widget2 дії від widget1. Таким чином, ви робите віджет1 лише вогневі дії, націлені на себе, а сага - це "фоновий процес", який слухає дії віджета1 і може відправляти дії, націлені на віджет2. Сага є точкою з’єднання між двома віджетами, але віджети залишаються роз'єднаними.
Якщо вам цікаво, подивіться на мою відповідь тут
Висновок
Якщо ви хочете побачити приклад того самого маленького додатка, що використовує ці різні стилі, перевірте гілки цього сховища .
Я не знаю, що найкращий варіант у довгостроковій перспективі, але мені дуже подобається, як Flux виглядає як джерело подій.
Якщо ви не знаєте понять, що сприяють подіям , погляньте на цей дуже педагогічний блог: Перетворивши базу даних зсередини за допомогою апача Samza , її потрібно прочитати, щоб зрозуміти, чому Flux приємно (але це може стосуватися і FRP )
Я думаю, що громада погоджується з тим, що найбільш перспективною реалізацією Flux є Redux , який поступово дозволить отримати дуже продуктивний досвід розробників завдяки гарячому перезавантаженню. Можливе вражаюче livecoding ala Bret Victor's Inventing on Principle video !