We need to use a react-redux
package to accomplish this connection between Redux and React.
The ReactRedux provides a small API with two key features: Provider
and connect
.
The Provider
is a wrapper component from ReactRedux that wraps your React app. This wrapper then
allows you to access the Redux store
and dispatch
function throughout your component tree.
Provider
takes two props, the Redux store and the child components of your app.
const Provider = ReactRedux.Provider;
class Root extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Provider store={ store }>
<App />
</Provider>
)
}
}
The Provider
component allows you to provide state
and dispatch
to your React components, but you must specify exactly what state and actions you want. This way, you make sure that each component only has access to the state it needs. You accomplish this by creating two functions mapStateToProps()
and mapDispatchToProps()
.
In these functions, you declare what pieces of state you want to have access to and which action creators you need to be able to dispatch.
Note: Behind the scenes, React Redux uses the store.subscribe()
method to implement mapStateToProps()
.
function mapStateToProps(state) {
return {
messages: state
}
}
The mapDispatchToProps()
function is used to provide specific action creators to your React components so they can dispatch actions against the redux store. It returns an object that maps dispatch actions to property names, which become component props
.
Therefore, each property returns a function taht calls dispatch
because it's passed in to mapDispatchToProps()
as a parameter when you define the function. Behind the scenes, React Redux is using Redux's store.dispatch()
to conduct these dispatches with mapDispatchToProps()
.
Note: Behind the scenes, React Redux uses the store.subscribe()
method to implement mapDispatchToProps()
.
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
// change code below this line
function mapDispatchToProps(dispatch) {
return {
submitNewMessage: function(message) {
dispatch(addMessage(message));
}
}
}
Now that you've written both the mapStateToProps()
and the mapDispatchToProps()
functions, you can use them to map state
and dispatch
to the props
of one of your React components. The connect
method from React Redux can handle this task. This method takes two optional arguments, mapStateToProps()
and mapDispatchToProps()
.
They are optional because you may have a component that only needs access to state
but doesn't need to dispatch any actions, or vice versa.
To use this method, pass in the functions as arguments, and immediately call the result with your component. This syntax is a little unusual and looks like:
connect(mapStateToProps, mapDispatchToProps)(MyComponent);
Note: if you want to omit one of the arguments to the connect
method, you pass null
in it's place.
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
const mapStateToProps = (state) => {
return {
messages: state
}
};
function mapDispatchToProps(dispatch) {
return {
submitNewMessage: function(message) {
dispatch(addMessage(message));
}
}
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h3>This is a Presentational Component</h3>
}
};
const connect = ReactRedux.connect;
const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(MyComponent)
const store = Redux.createStore(messageReducer);
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
const ContainerComponent = connect(
mapStateToProps,
mapDispatchToProps
)(AppComponent);
class RootComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Provider store={store}>
<ContainerComponent />
<Provider />
)
}
}
Now that Redux is connected, you need to extract the state management out of the MyComponent
component and into Redux. Currently, you have Redux connected, but you are handling the state locally within the MyComponent
component.
In the MyComponent
component, first, remove the messages
property in the local state
.
this.state = {
input: '',
messages: [],
};
this.state = {
input: '',
};
These messages will be managed by Redux. Next, modify the submitMessage()
method so that it dispatches submitNewMessage()
from this.props
, and pass in the current message input from local state
as an argument. Because you removed messages
from local state, remove the messages
property from the call to this.setState()
here as well.
submitMessage() {
const currentMessage = this.state.input;
this.setState({
input: '',
messages: [...this.state.messages, currentMessage]
});
}
submitMessage() {
this.props.submitNewMessage(this.state.input);
this.setState({
input: ''
});
}
Finally, modify, the render()
method so that it maps over messages received from props
rather than state
.
render() {
return (
<div>
<ul>
{ this.state.messages.map((message, idx) => <li key={idx}>{message}</li>) }
</ul>
</div>
);
}
render() {
return (
<div>
<ul>
{ this.props.messages.map((message, idx) => <li key={idx}>{message}</li>) }
</ul>
</div>
);
}
setup React and Redux
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './redux/reducers';
import App from './components/App';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
ReactDOM.render(
<Provider store={ store}>
<App />
<Provider />,
document.getElementById('root')
);