Skip to main content

Доступ к хранилищу(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>
)
*/
}

Дополнительные ресурсы