Я переписываю свое приложение для использования Flux, и у меня возникла проблема с получением данных из магазинов. У меня много компонентов, и они много гнездятся. Некоторые из них большие ( 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>
)
}
});
Минусы этого подхода:
- Прискорбно, что сотни компонентов потенциально подписываются на магазины;
- Это трудно следить за тем, как данные обновляются и в каком порядке , поскольку каждый компонент извлекает свои данные независимо друг от друга;
- Даже если у вас уже может быть сущность в состоянии, вы вынуждены передать ее идентификатор дочерним элементам, которые получат ее снова (или же нарушат согласованность).
Все данные считываются один раз на верхнем уровне и передаются компонентам
Когда мне надоело выявлять ошибки, я попытался перевести все извлечение данных на верхний уровень. Однако это оказалось невозможным, потому что для некоторых сущностей у меня есть несколько уровней вложенности.
Например:
- A
Category
содержитUserAvatar
s людей, которые участвуют в этой категории; Article
Может иметь несколькоCategory
сек.
Поэтому, если бы я хотел получить все данные из магазинов на уровне Article
, мне нужно было бы:
- Получить статью из
ArticleStore
; - Получить все категории статей из
CategoryStore
; - Отдельно получить участников каждой категории из
UserStore
; - Каким-то образом передать все эти данные компонентам.
Еще более досадно то, что всякий раз, когда мне нужна глубоко вложенная сущность, мне нужно было бы добавить код на каждый уровень вложенности, чтобы дополнительно передать ее вниз.
Подведение итогов
Оба подхода кажутся ошибочными. Как мне решить эту проблему наиболее элегантно?
Мои цели:
У магазинов не должно быть безумного количества подписчиков. Глупо каждому
UserLink
слушать,UserStore
если родительские компоненты уже это делают.Если родительский компонент получил какой-либо объект из магазина (например
user
), я не хочу, чтобы какие-либо вложенные компоненты снова извлекали его. Я смогу передать это через реквизит.Мне не нужно извлекать все сущности (включая отношения) на верхнем уровне, потому что это усложнит добавление или удаление отношений. Я не хочу вводить новые свойства на всех уровнях вложенности каждый раз, когда вложенная сущность получает новое отношение (например, категория получает a
curator
).