Perhaps I am not wrapping my head around redux, but all the examples I've seen don't really access state too much between containers and so I haven't seen much usage of store.getState(), but even if you want to dispatch, you need access to store, right?
So, other than importing import store from 'path/to/store/store'
in every file that I want to getState() or "dispatch", how do I get access to that state because if I don't include it, store is undefined.
It's simple to get access to the store inside a React component – no need to pass the store as a prop or import it, just use the connect function from React Redux, and supply a mapStateToProps function that pulls out the data you need. Then, inside the component, you can pass that data to a function that needs it.
Some valid reasons for using multiple stores in Redux might include: Solving a performance issue caused by too frequent updates of some part of the state, when confirmed by profiling the app.
Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state. Using local component state is fine.
In a react-redux application, it is a good practice to have only one redux store. But if for some weird/“special” reason if you have to have more than one store, you will face some problems.
In general you want to only make top-level container components ones that have access to the store - they will pass down any necessary data or action dispatches as props to their children components. This is the difference between a "smart" and a "dumb" component - "smart" components know about the Redux store/state, while "dumb" components just get props passed to them and have no idea about the bigger application state.
However, even just passing the store to container components can become tedious. That's why React-Redux provides one component out of the box that wraps your entire application. Check it out in the docs. This is the Provider
component and when you wrap your whole app with it, you only pass the store to a component once:
import createStore from '../store';
const store = createStore()
class App extends Component {
render() {
return (
<Provider store={store}>
<MainAppContainer />
</Provider>
)
}
}
As you can see here, I have a separate configuration file just for my store as there is a lot of modification you can do and for any remotely complex app, you'll find yourself doing the same for things like using compose to apply middleware.
Then any of your remaining "smart" components (generally wrappers) need to listen to the store. This is accomplished using the connect method. This allows you to map pieces of the state to your component properties as well as dispatch actions as properties.
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from './actionCreators';
const mapStateToProps = function(state){
return {
something: state.something,
}
}
const mapDispatchToProps = function (dispatch) {
return bindActionCreators({
getSomething: actionCreators.getSomething,
}, dispatch)
}
class MainAppContainer extends Component {
componentDidMount() {
//now has access to data like this.props.something, which is from store
//now has access to dispatch actions like this.props.getSomething
}
render() {
//will pass down store data and dispatch actions to child components
return (
<div>
<ChildComponent1 something={this.props.something} />
<ChildComponent2 getSomething={this.props.getSomething} />
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MainAppContainer)
Because you are always passing down dispatch actions and data to your children component as properties, you just reference those on that component with this.props
.
Building off the example above, you'll see that because I passed this.props.something
to ChildComponent1
, it has access to the something
data from the store but it does not have access to the getSomething
dispatch action. Likewise, ChildComponent2
only has access to the getSomething
dispatch action but not the something
data. This means that you only expose components to exactly what they need from the store.
For example, because ChildComponent2
was passed down the dispatch action as getSomething
, in my onClick
I can call this.props.getSomething
and it will call the dispatch action without needing any access to the store. In the same way it can continue to pass down getSomething
to another child component and that component could call it and/or pass it down and the cycle could continue indefinitely.
class ChildComponent2 extends Component {
render() {
return (
<div>
<div onClick={this.props.getSomething}>Click me</div>
<NestedComponent getSomething={this.props.getSomething} />
</div>
)
}
}
Edit from the comments
While this doesn't pertain directly to the question, in the comments you seemed a little confused about actions. I did not actually define the action getSomething
here. Instead it is usual in Redux apps to put all of your action definitions in a separate file called actionCreators.js
. This contains functions that are named the same as your actions and return an object with a type
property and any other methods/data that action requires. For instance, here's a very simple example actionCreators.js
file:
export function getSomething() {
return {
type: 'GET_SOMETHING',
payload: {
something: 'Here is some data'
}
}
}
This action type is what your reducer would listen for to know which action was being fired.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With