23
React and Redux: Part 2
- Hello again! Let's continue forward with the last of these lessons for this particular section.
- In the last post we created Redux store to handle the messages array and created an action for adding new messages. Now we need to provide React access to the Redux store and the actions it needs to dispatch updates.
- React Redux provides a small API with two key features:
Provider
andconnect
. - Here we'll focus on
Provider
. It's a wrapper component from React Redux that wraps your React app. Which then allows us to access the Reduxstore
anddispatch
functions throughout our component tree. -
Provider
takes two props, the Redux store and the child components of our app. - It will look like this.
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
const Provider = ReactRedux.Provider;
class AppWrapper extends React.Component {
// Render the Provider below this line
render() {
return (
<Provider store={store}>
<DisplayMessages/>
</Provider>
)
}
};
As you can see, we rendered the
Provider
fromReactRedux
and passed the Redux store as a prop. Then we rendered theDisplayMessages
component as a child. Once we finished, we can see our React component rendered to the page.Obviously there's a lot more to learn about this but I'm just basics.
- The
Provider
component allows you to providestate
anddispatch
to your React components, but you must specify exactly what state and actions you want. - You can accomplish this by creating two functions:
mapStateToProps()
andmapDispatchToProps()
. - With 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.
- FreeCodeCamp wants us to create a function
mapStateToProps
. For this function it will takestate
as an argument, then return an object which maps that state to specific property names. Then create a propertymessages
in the object that's being returned and set it tostate
.
const state = [];
function mapStateToProps(state) {
return {
messages: state
}
}
- The properties become available to our component via
props
. FreeCodeCamp provides us with thestore.subscribe
method to implementmapStateToProps()
behind the scenes.
- The
mapDispatchToProps()
function is used to provide specific action creators to your React components so they can dispatch actions against the Redux store. It also returns an object that maps dispatch actions to property names, which become componentprops
. With that, each property returns a function that callsdispatch
with an action creator and any relevant action data. - We have access to this dispatch because it's passed in to
mapDispatchToProps()
as a parameter when you define the function, just like you passedstate
tomapStateToProps()
- Behind the scenes, React Redux is using Redux's
store.dispatch
- Example, we have a
addMessage()
action creator that takes amessage
as an action payload. The object returned frommapDispatchToProps()
for this action creator would look like this.
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
function mapDispatchToProps(dispatch) {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message))
}
}
}
- The object here has a property
submitNewMessage
set to the dispatch function, which takes a parameter for the new message to add when it dispatchesaddMessage()
.
- Now that we've written both the
mapStateToProps
and themapDispatchToProps
functions, you can use them to mapstate
anddispatch
to theprops
of one of our React components. - The
connect
method has two optional arguments,mapStateToProps()
andmapDispatchToProps()
. 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 such method, you'll have to pass in the functions as arguments, and then call the result with our component. It will look like so:
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
- FreeCodeCamp wants us to connect the component
Presentational
to Redux with the connect method from theReactRedux
global object, and call it on thePresentational
component. Assigning the result to a newconst
calledConnectedComponent
that represents the connected component.
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
const mapStateToProps = (state) => {
return {
messages: state
}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message));
}
}
};
class Presentational extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h3>This is a Presentational Component</h3>
}
};
const connect = ReactRedux.connect;
- Answer:
const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational)
- Here the The Presentational component received a prop messages via connect and received a prop submitNewMessage via connect.
- With learning about how to use
connect
we can now apply what we've learned to our React Component that handles messages. - Container components are connected to Redux. These are typically responsible for dispatching actions to the store and pass store state to child components as props.
- Here FreeCodeCamp wants us to create a new component const called
Container
that usesconnect
to connect thePresentational
component to Redux. Then, in theAppWrapper
, render the React Redux Provider component. Pass Provider the Redux store as a prop and renderContainer
as a child. Once everything is setup, you will see the messages app rendered to the page.
const mapStateToProps = (state) => {
return { messages: state }
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (newMessage) => {
dispatch(addMessage(newMessage))
}
}
};
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// Define the Container component here:
class AppWrapper extends React.Component {
constructor(props) {
super(props);
}
render() {
// Complete the return statement:
return (null);
}
};
- Answer:
const mapStateToProps = (state) => {
return { messages: state }
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (newMessage) => {
dispatch(addMessage(newMessage))
}
}
};
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational)
class AppWrapper extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
- Nice work! Just like me you're learning about React and Redux and all it's wonders this isn't the end though. There's still so much we have to work towards and learn more about. Please don't give up on making things happen.
- Let's recall that you wrote all the Redux code so that Redux could control the state management of your React messages app. Now that Redux is connected, you need to extract the state management out of the Presentational component and into Redux. We have Redux connected, but we are handling the state locally within the Presentational component.
- FreeCodeCamp wants us In the Presentational component, remove the
messages
property in the local state. These messages will be managed by Redux. - Next, modify the
submitMessage()
method so that it dispatchessubmitNewMessage()
fromthis.props
, and pass in the current message input from localstate
as an argument. Because you removed messages from local state, remove the messages property from the call tothis.setState()
here as well. Finally, modify therender()
method so that it maps over the messages received fromprops
rather thanstate
.
submitMessage() {
this.setState((state) => ({
input: '',
messages: state.messages.concat(state.input)
}));
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
- Answer:
submitMessage() {
this.props.submitNewMessage(this.state.input)
this.setState({
input: ''
})
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.props.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
- And that is it for the React and Redux Lesson on freeCodeCamp. Thank you so much for reading these as I go through them. Helping myself and others,
Larson, Q., 2019. Frontend Development Libraries. [online] Freecodecamp.org. Available at: https://www.freecodecamp.org/learn/front-end-development-libraries/react-and-redux
This can get complicated quickly. there's a link below if you want to experiment on your own machine, the Create React App comes configured and ready to go.
23