I'm trying to add a listener to my store. Every example I find online seems to use store.subscribe(() => function here)
However, I cannot access 'store' from any of my non-root components. I've found quote a few questions about it (How to access store in second component in react-redux) but they only talk about accessing props/actions using the Provider=store
HOC and not access to the store itself to do things like adding listeners to the store.
(for my specific use case, I want to listen to the store to if the 'activeUser' changes, and if it does, fire off a chain of additional actions. I believe this can likely be solved with thunk and simply chaining actions to the "setActiveUser" action... so this question is a bit more about how to actually get listeners to a store than this specific problem)
What you're trying to achieve is imperative programming. react-redux is designed around the declarative nature of React and therefore prevents your components from accessing the store directly.
A dirty solution would be to export the store from the Javascript module it was created in, making it accessible by any other module of your application. That way you can make your subscriptions anywhere, not just your 'root component'. This is however an anti-pattern and should be avoided.
The best solution for your problem is to embrace the declarative nature of React and do something like this :
MyComponentContainer.js
import {connect} from 'react-redux';
import {MyComponent} from './MyComponent';
function mapStateToProps(state) {
return {
activeUser: /* extract the active user from the state object */
};
}
export const MyComponentContainer = connect(mapStateToProps)(MyComponent)
MyComponent.js
import React from 'react';
export class MyComponent extends React.Component {
componentDidMount() {
this.yourChainOfActions(this.props.activeUser);
}
componentDidUpdate(prevProps) {
if(this.props.activeUser.id !== prevProps.activeUser.id) {
this.yourChainOfActions(this.props.activeUser);
}
}
yourChainOfActions = (activeUser) => {
// ...
};
render() {
// ...
}
}
This requires a bit of mind shifting, but it is the best way to imperatively react to changes with React (until hooks come along)
--EDIT--
If "yourChainOfActions" consists in a bunch of store.dispatch()
calls, you need to give MyComponent
access to the store.dispatch
function. Normally, you would not pass this function directly, and you would rather create a wrapper in MyComponentContainer
's mapDispatchToProps
, as such :
MyComponentContainer.js
import {connect} from 'react-redux';
import {MyComponent} from './MyComponent';
function mapStateToProps(state) {
return {
activeUser: /* extract the active user from the state object */
};
}
function mapDispatchToProps(dispatch) {
return {
onActiveUserChange(activeUser) {
// use dispatch however you wish here
}
}
}
export const MyComponentContainer = connect(mapStateToProps, mapDispatchToProps)(MyComponent)
MyComponent
will now have access to the onActiveUserChange
function through its props, that you can use in its componentDidUpdate
lifecycle.
You may decide to split the onActiveUserChange
into a buch of separate functions for better composability. It is up to you and your use cases.
The quick answer is don't do it in your component!
Usually this would be done when you're defining/building your store. Maybe you have a use case for coupling this subscription to a component, but I'd be very surprised if so.
With this solution, the store looks after itself, and the components are entirely passive to it - merely firing actions and receiving reduced data. They shouldn't have an impact on the business logic of your data flow.