|
| 1 | +--- |
| 2 | +id: accessing-store |
| 3 | +title: Accessing the Store |
| 4 | +hide_title: true |
| 5 | +sidebar_label: Accessing the Store |
| 6 | +--- |
| 7 | + |
| 8 | +# Accessing the Store |
| 9 | + |
| 10 | +React Redux provides APIs that allow your components to dispatch actions and subscribe to data updates from the store. |
| 11 | + |
| 12 | +As part of that, React Redux abstracts away the details of which store you are using, and the exact details of how that |
| 13 | +store interaction is handled. In typical usage, your own components should never need to care about those details, and |
| 14 | +won't ever reference the store directly. React Redux also internally handles the details of how the store and state are |
| 15 | +propagated to connected components, so that this works as expected by default. |
| 16 | + |
| 17 | +However, there may be certain use cases where you may need to customize how the store and state are propagated to |
| 18 | +connected components, or access the store directly. Here are some examples of how to do this. |
| 19 | + |
| 20 | +## Understanding Context Usage |
| 21 | + |
| 22 | +Internally, React Redux uses [React's "context" feature](https://reactjs.org/docs/context.html) to make the |
| 23 | +Redux store accessible to deeply nested connected components. As of React Redux version 6, this is normally handled |
| 24 | +by a single default context object instance generated by `React.createContext()`, called `ReactReduxContext`. |
| 25 | + |
| 26 | +React Redux's `<Provider>` component uses `<ReactReduxContext.Provider>` to put the Redux store and the current store |
| 27 | +state into context, and `connect` uses `<ReactReduxContext.Consumer>` to read those values and handle updates. |
| 28 | + |
| 29 | +## Providing Custom Context |
| 30 | + |
| 31 | +Instead of using the default context instance from React Redux, you may supply your own custom context instance. |
| 32 | + |
| 33 | +```js |
| 34 | +<Provider context={MyContext} store={store}> |
| 35 | + <App /> |
| 36 | +</Provider> |
| 37 | +``` |
| 38 | + |
| 39 | +If you supply a custom context, React Redux will use that context instance instead of the one it creates and exports by default. |
| 40 | + |
| 41 | +After you’ve supplied the custom context to `<Provider />`, you will need to supply this context instance to all of your connected components that are expected to connect to the same store: |
| 42 | + |
| 43 | +```js |
| 44 | +// You can pass the context as an option to connect |
| 45 | +export default connect( |
| 46 | + mapState, |
| 47 | + mapDispatch, |
| 48 | + null, |
| 49 | + { context: MyContext } |
| 50 | +)(MyComponent) |
| 51 | + |
| 52 | +// or, call connect as normal to start |
| 53 | +const ConnectedComponent = connect( |
| 54 | + mapState, |
| 55 | + mapDispatch |
| 56 | +)(MyComponent) |
| 57 | + |
| 58 | +// Later, pass the custom context as a prop to the connected component |
| 59 | +<ConnectedComponent context={MyContext} /> |
| 60 | +``` |
| 61 | + |
| 62 | +The following runtime error occurs when React Redux does not find a store in the context it is looking. For example: |
| 63 | + |
| 64 | +- You provided a custom context instance to `<Provider />`, but did not provide the same instance (or did not provide any) to your connected components. |
| 65 | +- You provided a custom context to your connected component, but did not provide the same instance (or did not provide any) to `<Provider />`. |
| 66 | + |
| 67 | +> Invariant Violation |
| 68 | +> |
| 69 | +> 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. |
| 70 | +
|
| 71 | +## Multiple Stores |
| 72 | + |
| 73 | +[Redux was designed to use a single store](https://redux.js.org/api/store#a-note-for-flux-users). |
| 74 | +However, if you are in an unavoidable position of needing to use multiple stores, with v6 you may do so by providing (multiple) custom contexts. |
| 75 | +This also provides a natural isolation of the stores as they live in separate context instances. |
| 76 | + |
| 77 | +```js |
| 78 | +// a naive example |
| 79 | +const ContextA = React.createContext(); |
| 80 | +const ContextB = React.createContext(); |
| 81 | + |
| 82 | +// assuming reducerA and reducerB are proper reducer functions |
| 83 | +const storeA = createStore(reducerA); |
| 84 | +const storeB = createStore(reducerB); |
| 85 | + |
| 86 | +// supply the context instances to Provider |
| 87 | +function App() { |
| 88 | + return ( |
| 89 | + <Provider store={storeA} context={ContextA} /> |
| 90 | + <Provider store={storeB} context={ContextB}> |
| 91 | + <App /> |
| 92 | + </Provider> |
| 93 | + </Provider> |
| 94 | + ); |
| 95 | +} |
| 96 | + |
| 97 | +// fetch the corresponding store with connected components |
| 98 | +// you need to use the correct context |
| 99 | +connect(mapStateA, null, null, { context: ContextA })(MyComponentA) |
| 100 | + |
| 101 | +// You may also pass the alternate context instance directly to the connected component instead |
| 102 | +<ConnectedMyComponentA context={ContextA} /> |
| 103 | + |
| 104 | +// it is possible to chain connect() |
| 105 | +// in this case MyComponent will receive merged props from both stores |
| 106 | +compose( |
| 107 | + connect(mapStateA, null, null, { context: ContextA }), |
| 108 | + connect(mapStateB, null, null, { context: ContextB }) |
| 109 | +)(MyComponent); |
| 110 | +``` |
| 111 | + |
| 112 | +## Using `ReactReduxContext` Directly |
| 113 | + |
| 114 | +In rare cases, you may need to access the Redux store directly in your own components. This can be done by rendering |
| 115 | +the appropriate context consumer yourself, and accessing the `store` field out of the context value. |
| 116 | + |
| 117 | +> **Note**: This is **_not_ considered part of the React Redux public API, and may break without notice**. We do recognize |
| 118 | +> that the community has use cases where this is necessary, and will try to make it possible for users to build additional |
| 119 | +> functionality on top of React Redux, but our specific use of context is considered an implementation detail. |
| 120 | +> If you have additional use cases that are not sufficiently covered by the current APIs, please file an issue to discuss |
| 121 | +> possible API improvements. |
| 122 | +
|
| 123 | +```js |
| 124 | +import { ReactReduxContext } from 'react-redux' |
| 125 | + |
| 126 | +// in your connected component |
| 127 | +function MyConnectedComponent() { |
| 128 | + return ( |
| 129 | + <ReactReduxContext.Consumer> |
| 130 | + {({ store }) => { |
| 131 | + // do something useful with the store, like passing it to a child |
| 132 | + // component where it can be used in lifecycle methods |
| 133 | + }} |
| 134 | + </ReactReduxContext.Consumer> |
| 135 | + ) |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +## Further Resources |
| 140 | + |
| 141 | +- CodeSandbox example: [A reading list app with theme using a separate store](https://codesandbox.io/s/92pm9n2kl4), implemented by providing (multiple) custom context(s). |
| 142 | +- Related issues: |
| 143 | + - [#1132: Update docs for using a different store key](https://github.com/reduxjs/react-redux/issues/1132) |
| 144 | + - [#1126: `<Provider>` misses state changes that occur between when its constructor runs and when it mounts](https://github.com/reduxjs/react-redux/issues/1126) |
0 commit comments