Ответ 1
tl; dr лучше всего использовать хранилище типа redux
или mobx
при управлении состоянием, которое должно быть доступно во всех приложениях. Эти библиотеки позволяют вашим компонентам подключаться/следить за состоянием и постоянно обновлять любые изменения состояния.
Что такое <Route>
?
Причина, по которой вы не можете передавать реквизит через компоненты <Route>
, заключается в том, что они не являются реальными компонентами в том смысле, что они ничего не отображают. Вместо этого они используются для создания объекта конфигурации маршрута.
Это означает, что это:
<Router history={browserHistory}>
<Route path='/' component={App}>
<Route path='foo' component={Foo} />
</Route>
</Router>
эквивалентно этому:
<Router history={browserHistory} routes={{
path: '/',
component: App,
childRoutes: [
{
path: 'foo',
component: Foo
}
]
}} />
Маршруты оцениваются только на исходном монтировании, поэтому вы не можете передавать им новые реквизиты.
Статические реквизиты
Если у вас есть статические реквизиты, которые вы хотите передать в ваш магазин, вы можете создать свой собственный компонент более высокого порядка, который будет вводить их в магазин. К сожалению, это работает только для статических реквизитов, потому что, как указано выше, <Route>
оцениваются только один раз.
function withProps(Component, props) {
return function(matchProps) {
return <Component {...props} {...matchProps} />
}
}
class MyApp extends React.Component {
render() {
return (
<Router history={browserHistory}>
<Route path='/' component={App}>
<Route path='foo' component={withProps(Foo, { test: 'ing' })} />
</Route>
</Router>
)
}
}
Использование location.state
location.state
- удобный способ передачи состояния между компонентами при навигации. Однако у него есть один существенный недостаток, заключающийся в том, что государство существует только при навигации по вашей заявке. Если пользователь следит за ссылкой на ваш сайт, государство не будет прикреплено к местоположению.
Использование магазина
Итак, как вы передаете данные на ваш маршрут component
s? Обычный способ - использовать хранилище типа redux
или mobx
. С помощью redux
вы можете connect
добавить свой компонент в хранилище, используя компонент более высокого порядка. Затем, когда ваш маршрут component
(который является действительно HOC с вашим компонентом маршрута в качестве его дочернего элемента), он может получить последнюю информацию из магазина.
const Foo = (props) => (
<div>{props.username}</div>
)
function mapStateToProps(state) {
return {
value: state.username
};
}
export default connect(mapStateToProps)(Foo)
Я не особенно знаком с mobx
, но, по моему мнению, его еще проще настроить. Использование redux
, mobx
или одно из других состояний управления - отличный способ передать состояние во всем приложении.
Примечание. Здесь вы можете прекратить чтение. Ниже приведены вероятные примеры для передачи состояния, но вы, вероятно, должны просто использовать библиотеку хранилища.
Без магазина
Что делать, если вы не хотите использовать магазин? Вам не повезло? Нет, но вы должны использовать экспериментальную функцию для React: context
. Чтобы использовать context
, один из ваших родительских компонентов должен явно определить метод getChildContext
, а также объект childContextTypes
. Любой дочерний компонент, который хочет получить доступ к этим значениям через context
, тогда должен будет определить объект contextTypes
(аналогичный propTypes
).
class MyApp extends React.Component {
getChildContext() {
return {
username: this.state.username
}
}
}
MyApp.childContextTypes = {
username: React.PropTypes.object
}
const Foo = (props, context) => (
<div>{context.username}</div>
)
Foo.contextTypes = {
username: React.PropTypes.object
}
Вы даже можете написать свой собственный компонент более высокого порядка, который автоматически вводит значения context
в качестве реквизитов вашего <Route>
component
s. Это будет что-то вроде "магазина бедных людей". Вы можете заставить его работать, но, скорее всего, менее эффективно и с большим количеством ошибок, чем с использованием одной из вышеупомянутых библиотек магазинов.
Как насчет React.cloneElement
?
Существует еще один способ предоставить реквизиты <Route>
component
, но он работает только на одном уровне за раз. По сути, когда React Router обрабатывает компоненты на основе текущего маршрута, он сначала создает элемент для наиболее глубоко вложенного совпадающего <Route>
. Затем он передает этот элемент в качестве children
prop при создании элемента для следующего наиболее глубоко вложенного <Route>
. Это означает, что в методе render
второго компонента вы можете использовать React.cloneElement
для клонирования существующего элемента children
и добавления дополнительных реквизитов к нему.
const Bar = (props) => (
<div>These are my props: {JSON.stringify(props)}</div>
)
const Foo = (props) => (
<div>
This is my child: {
props.children && React.cloneElement(props.children, { username: props.username })
}
</div>
)
Это, конечно, утомительно, особенно если вам нужно будет передавать эту информацию через несколько уровней <Route>
component
s. Вам также необходимо будет управлять своим состоянием в базовом компоненте <Route>
(т.е. <Route path='/' component={Base}>
), потому что у вас не было бы способа ввести состояние из исходных компонентов <Router>
.