Я переписываю свое приложение для использования Flux, и у меня есть проблема с извлечением данных из Stores. У меня много компонентов, и они много гнездятся. Некоторые из них большие (Article
), некоторые небольшие и простые (UserAvatar
, UserLink
).
Я боролся с тем, где в иерархии компонентов я должен читать данные из магазинов.
Я пробовал два крайних подхода, ни один из которых мне не нравился:
Все компоненты сущности считывают свои собственные данные
Каждый компонент, который нуждается в некоторых данных из Store, получает только идентификатор объекта и сам получает объект.
Например, Article
передается articleId
, UserAvatar
и UserLink
передаются userId
.
Этот подход имеет несколько существенных недостатков (обсуждаемых в примере кода).
var Article = React.createClass({
mixins: [createStoreMixin(ArticleStore)],
propTypes: {
articleId: PropTypes.number.isRequired
},
getStateFromStores() {
return {
article: ArticleStore.get(this.props.articleId);
}
},
render() {
var article = this.state.article,
userId = article.userId;
return (
<div>
<UserLink userId={userId}>
<UserAvatar userId={userId} />
</UserLink>
<h1>{article.title}</h1>
<p>{article.text}</p>
<p>Read more by <UserLink userId={userId} />.</p>
</div>
)
}
});
var UserAvatar = React.createClass({
mixins: [createStoreMixin(UserStore)],
propTypes: {
userId: PropTypes.number.isRequired
},
getStateFromStores() {
return {
user: UserStore.get(this.props.userId);
}
},
render() {
var user = this.state.user;
return (
<img src={user.thumbnailUrl} />
)
}
});
var UserLink = React.createClass({
mixins: [createStoreMixin(UserStore)],
propTypes: {
userId: PropTypes.number.isRequired
},
getStateFromStores() {
return {
user: UserStore.get(this.props.userId);
}
},
render() {
var user = this.state.user;
return (
<Link to='user' params={{ userId: this.props.userId }}>
{this.props.children || user.name}
</Link>
)
}
});
Недостатки этого подхода:
- Это разочаровывает наличие компонентов 100s, которые потенциально могут быть подписаны на магазины;
- трудно отслеживать, как данные обновляются и в каком порядке, потому что каждый компонент самостоятельно извлекает свои данные;
- Даже если у вас уже есть сущность в состоянии, вы вынуждены передавать свой идентификатор детям, которые снова получат его (или нарушают согласованность).
Все данные считываются один раз на верхнем уровне и передаются компонентам
Когда я устал отслеживать ошибки, я попытался поместить все данные на верхний уровень. Это, однако, оказалось невозможным, потому что для некоторых объектов у меня несколько уровней вложенности.
Например:
- A
Category
содержитUserAvatar
людей, которые вносят вклад в эту категорию; -
Article
может иметь несколькоCategory
s.
Поэтому, если бы я хотел получить все данные из Stores на уровне Article
, мне нужно было бы:
- Получить статью из
ArticleStore
; - Извлеките все категории статей из
CategoryStore
; - Отдельно извлекайте вкладчиков каждой категории из
UserStore
; - Как-то передать все эти данные в компоненты.
Еще более разочаровывающе, когда мне нужна глубоко вложенная сущность, мне нужно будет добавить код для каждого уровня вложенности, чтобы дополнительно передать его.
Подведение итогов
Оба подхода кажутся ошибочными. Как я могу решить эту проблему наиболее элегантно?
Мои цели:
-
Магазины не должны иметь безумного количества подписчиков. Это глупо для каждого
UserLink
для прослушиванияUserStore
, если это уже делают родительские компоненты. -
Если родительский компонент извлек некоторый объект из хранилища (например,
user
), я не хочу, чтобы вложенные компоненты приходили к нему снова. Я должен был бы передать его через реквизит. -
Мне не нужно будет получать все сущности (включая отношения) на верхнем уровне, потому что это усложнит добавление или удаление отношений. Я не хочу вводить новые реквизиты на всех уровнях вложенности каждый раз, когда вложенный объект получает новые отношения (например, категория получает
curator
).