Доступ к хранилищу(store)
React Redux предоставляет API, которые позволяют вашим компонентам отправлять(dispatch) действия и подписываться на обновления данных хранилища(store).
В рамках этого, React Redux абстрагируется от сведений о том, какое хранилище вы используете, и как происходит взаимодействие с ним. В большинстве случаев ваши собственные компоненты не должны заботиться об этих деталях и никогда напрямую не ссылаются на хранилище(store). React Redux внутренне обрабатывает детали того, как хранилище(store) и состояние(state) передаются в cвязанные компоненты, так эта работа выполняется должным образом по умолчанию.
Тем не менее, могут быть определенные сценарии использования, когда вам может потребоваться настроить способ передачи хранилища(store) и состояния(state) в cвязанные компоненты или получить прямой доступ к хранилищу(store). Вот несколько примеров того, как это сделать.
Использования контекста
Внутри React Redux использует функцию "контекста" React, чтобы сделать хранилище Redux доступным для глубоко вложенных cвязанных компонентов. Начиная с версии 6 React Redux, это обычно обрабатывается одним экземпляром объекта контекста по умолчанию, сгенерированным React.createContext()
, называемым ReactReduxContext
.
Компонент <Provider>
из React Redux использует <ReactReduxContext.Provider>
, чтобы поместить Redux хранилище(store) и текущее
состояние(state) хранилища в контекст, а connect
использует <ReactReduxContext.Consumer>
, чтобы считывать эти значения и обрабатывать обновления.
Использование хука useStore
Хук useStore
возвращает текущий экземпляр хранилища из ReactReduxContext
. Если вам действительно нужно получить доступ к хранилищу(store),
рекомендуется использовать такой подход.
Предоставление собственного контекста
Вместо использования экземпляра контекста по умолчанию из React Redux вы можете предоставить свой собственный пользовательский экземпляр контекста.
<Provider context={MyContext} store={store}>
<App />
</Provider>
Если вы предоставляете собственный контекст, React Redux будет использовать этот экземпляр контекста вместо того, который он создает и экспортирует по умолчанию.
После предоставления пользовательского контекста в <Provider />
, вам нужно будет предоставить этот экземпляр контекста всем вашим cвязанным компонентам, которые, как ожидается,
будут подключаться к одному и тому же хранилищу(store):
// Вы можете предоставить контекст как параметр в connect
export default connect(
mapState,
mapDispatch,
null,
{ context: MyContext }
)(MyComponent)
// Или вызывать connect как обычно...
const ConnectedComponent = connect(
mapState,
mapDispatch
)(MyComponent)
// ...и затем предоставить пользовательский контекст
// как пропс в подключенный компонент
<ConnectedComponent context={MyContext} />
В случае, когда React Redux не находит хранилище(store) в своём контексте, возникает ошибка во время исполнения. Например:
- Вы предоставили собственный экземпляр контекста для
<Provider />
, но не предоставили такой же экземпляр (или не предоставили вообще) cвязанным компонентам. - Вы предоставили собственный контекст cвязанному компоненту, но не предоставили тот же экземпляр (или не предоставили его) для
<Provider />
.
Вид ошибка:
Invariant Violation
Could not find "store" in the context of "Connect(MyComponent)". Either wrap the root component in a
<Provider>
, or pass a custom React context provider to<Provider>
and the corresponding React context consumer to Connect(Todo) in connect options.
Собственный контекст и API хуков
Чтобы получить доступ к cобственному контексту через API хуков, вы можете создать собственные хуки с помощью функций создания хуков.
Несколько хранилищ(store)
Redux был разработан для использования с одним хранилищем(store). Однако, если вам неизбежно приходится использовать несколько хранилищ, начиная с версии 6, вы можете предоставив несколько собственных контекстов. Это обеспечивает изоляцию, поскольку хранилища(store) находятся в отдельных экземплярах контекста.
// Наивный пример
const ContextA = React.createContext();
const ContextB = React.createContext();
// Предполагаем, что reducerA и reducerB являются правильными редюсерами(reducer)
const storeA = createStore(reducerA);
const storeB = createStore(reducerB);
// Предоставляем экземпляры контекста в Provider
function App() {
return (
<Provider store={storeA} context={ContextA} />
<Provider store={storeB} context={ContextB}>
<RootModule />
</Provider>
</Provider>
);
}
// Чтобы получить ожидаемый экземпляр хранилища(store),
// используйте нужный контекст
connect(mapStateA, null, null, { context: ContextA })(MyComponentA)
// Вместо этого вы также можете предоставить альтернативный контекст напрямую в экземпляр
<ConnectedMyComponentA context={ContextA} />
// Возможно сделать цепочку вызовов connect().
// Так MyComponent будет получать пропсы из обоих хранилищ (stores)
compose(
connect(mapStateA, null, null, { context: ContextA }),
connect(mapStateB, null, null, { context: ContextB })
)(MyComponent);
Использование ReactReduxContext
напрямую
В редких случаях вам может потребоваться доступ к Redux хранилищу(store) непосредственно в ваших собственных компонентах. Это можно сделать путем рендеринга соответствующего потребителя контекста и доступа к полю store
из значения контекста.
Предупреждение
Это не считается частью общедоступного API React Redux и может перестать работать без предварительного уведомления. Мы признаем, что у сообщества есть случаи использования, где это необходимо, и постараемся сделать так, чтобы пользователи могли создавать дополнительную функциональность поверх React Redux, но наше особое использование контекста считается деталью реализации. Если у вас есть дополнительные варианты использования, которые недостаточно охвачены текущими API, задайте вопрос для обсуждения возможных улучшений API.
import { ReactReduxContext } from 'react-redux'
// Где-то внутри <Provider>
function MyConnectedComponent() {
// Доступ в хранилище(store) через хук `useContext`
const { store } = useContext(ReactReduxContext)
// В альтернативу используйте форму отрисовки пропсов из контекста
/*
return (
<ReactReduxContext.Consumer>
{({ store }) => {
// Логика с хранилищем(store): передача его дочерним компонентам,
// где он может быть использован в методах жизненного цикла
}}
</ReactReduxContext.Consumer>
)
*/
}
Дополнительные ресурсы
- Пример CodeSandbox: Приложение списка чтения с темой, использующее отдельное хранилище(store), реализованное путем предоставления пользовательского контекста или нескольких пользовательских контекстов.
- Связанные вопросы: