Skip to main content

Redux

Redux: Começando

npm i -D redux react-redux

add src/store/index.js :

import { createStore } from 'redux';
import { Reducers } from '../reducers';
export const Store = createStore(Reducers);
  • Crie o reducer em uma pasta separada :

src/reducers/index.js

import { clickReducer } from './clickReducer';
import { otherReducer } from './otherReducer';
import { combineReducers } from 'redux';
export const Reducers = combineReducers({
clickState: clickReducer,
otherState: otherReducer
});
  • Create the reducer src/reducer/clickReducer.js
import { CLICK_UPDATE_VALUE } from '../actions/actionTypes';
const initialState = {
newValue: ''
};
export const clickReducer = (state = initialState, action) => {
switch (action.type) {
case CLICK_UPDATE_VALUE:
return {
...state,
newValue: action.newValue
};
default:
return state;
}
};
  • Create the Action src/actions/index.js
import { CLICK_UPDATE_VALUE } from './actionTypes';
export const clickButton = value => ({
type: CLICK_UPDATE_VALUE,
newValue: value
});
  • Create the type src/actions/actionTypes.js
export const CLICK_UPDATE_VALUE = 'CLICK_UPDATE_VALUE';
  • Wrap app into a provider Store em src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import { Store } from './store';
ReactDOM.render(
<Provider store={Store}>
<App />
</Provider>
, document.getElementById('root'));
registerServiceWorker();
  • Connect app to store
import React, { Component } from 'react';
import { connect } from 'react-redux';
import './App.css';
class App extends Component {
render() {
const { newValue } = this.props;
return (
<div className="App" style={{ paddingTop: '10px' }}>
<input type='text' />
<button>
Click me!
</button>
<h1>{newValue}</h1>
</div>
);
}
}
const mapStateToProps = store => ({
newValue: store.clickState.newValue
});
export default connect(mapStateToProps)(App);

Redux: Create a Redux Store

Redux is a state management framework that can be used with a number of different web technologies, including React.

In Redux, there is a single state object that's responsible for the entire state of your application. This means if you had a React app with ten components, and each component had its own local state, the entire state of your app would be defined by a single state object housed in the Redux store. This is the first important principle to understand when learning Redux: the Redux store is the single source of truth when it comes to application state.

This also means that any time any piece of your app wants to update state, it must do so through the Redux store. The unidirectional data flow makes it easier to track state management in your app.


The Redux store is an object which holds and manages application state. There is a method called createStore() on the Redux object, which you use to create the Redux store. This method takes a reducer function as a required argument. The reducer function is covered in a later challenge, and is already defined for you in the code editor. It simply takes state as an argument and returns state.

Declare a store variable and assign it to the createStore() method, passing in the reducer as an argument.

Note: The code in the editor uses ES6 default argument syntax to initialize this state to hold a value of 5. If you're not familiar with default arguments, you can refer to the ES6 section in the Curriculum which covers this topic.

const reducer = (state = 5) => {
return state;
}

// Redux methods are available from a Redux object
// For example: Redux.createStore()
// Define the store here:
const store = Redux.createStore(reducer);

Redux: Get State from the Redux Store

The Redux store object provides several methods that allow you to interact with it. For example, you can retrieve the current state held in the Redux store object with the getState() method.


The code from the previous challenge is re-written more concisely in the code editor. Use store.getState() to retrieve the state from the store, and assign this to a new variable currentState.

const store = Redux.createStore(
(state = 5) => state
);

// Change code below this line

let currentState = store.getState();

Redux: Define a Redux Action

Since Redux is a state management framework, updating state is one of its core tasks. In Redux, all state updates are triggered by dispatching actions. An action is simply a JavaScript object that contains information about an action event that has occurred. The Redux store receives these action objects, then updates its state accordingly. Sometimes a Redux action also carries some data. For example, the action carries a username after a user logs in. While the data is optional, actions must carry a type property that specifies the 'type' of action that occurred.

Think of Redux actions as messengers that deliver information about events happening in your app to the Redux store. The store then conducts the business of updating state based on the action that occurred.


Writing a Redux action is as simple as declaring an object with a type property. Declare an object action and give it a property type set to the string 'LOGIN'.

// Define an action here:
const action = {
type: 'LOGIN'
}

Redux: Define an Action Creator

After creating an action, the next step is sending the action to the Redux store so it can update its state. In Redux, you define action creators to accomplish this. An action creator is simply a JavaScript function that returns an action. In other words, action creators create objects that represent action events.


Define a function named actionCreator() that returns the action object when called.

const action = {
type: 'LOGIN'
}
// Define an action creator here:
function actionCreator(){
return action;
}

Redux: Dispatch an Action Event

dispatch method is what you use to dispatch actions to the Redux store. Calling store.dispatch() and passing the value returned from an action creator sends an action back to the store.

Recall that action creators return an object with a type property that specifies the action that has occurred. Then the method dispatches an action object to the Redux store. Based on the previous challenge's example, the following lines are equivalent, and both dispatch the action of type LOGIN:

store.dispatch(actionCreator());
store.dispatch({ type: "LOGIN" });

The Redux store in the code editor has an initialized state that's an object containing a login property currently set to false. There's also an action creator called loginAction() which returns an action of type LOGIN. Dispatch the LOGIN action to the Redux store by calling the dispatch method, and pass in the action created by loginAction().

const store = Redux.createStore(
(state = {login: false}) => state
);

const loginAction = () => {
return {
type: 'LOGIN'
}
};

// Dispatch the action here:
store.dispatch(loginAction());

Redux: Handle an Action in the Store

After an action is created and dispatched, the Redux store needs to know how to respond to that action. This is the job of a reducer function. Reducers in Redux are responsible for the state modifications that take place in response to actions. A reducer takes state and action as arguments, and it always returns a new state. It is important to see that this is the only role of the reducer. It has no side effects — it never calls an API endpoint and it never has any hidden surprises. The reducer is simply a pure function that takes state and action, then returns new state.

Another key principle in Redux is that state is read-only. In other words, the reducer function must always return a new copy of state and never modify state directly. Redux does not enforce state immutability, however, you are responsible for enforcing it in the code of your reducer functions. You'll practice this in later challenges.


The code editor has the previous example as well as the start of a reducer function for you. Fill in the body of the reducer function so that if it receives an action of type 'LOGIN' it returns a state object with login set to true. Otherwise, it returns the current state. Note that the current state and the dispatched action are passed to the reducer, so you can access the action's type directly with action.type.

const defaultState = {
login: false
};

const reducer = (state = defaultState, action) => {
// Change code below this line
switch (action.type) {
case 'LOGIN':
return{
login:true
}
break;
default:
return state;
}
// Change code above this line
};

const store = Redux.createStore(reducer);

const loginAction = () => {
return {
type: 'LOGIN'
}
};

Redux: Use a Switch Statement to Handle Multiple Actions

You can tell the Redux store how to handle multiple action types. Say you are managing user authentication in your Redux store. You want to have a state representation for when users are logged in and when they are logged out. You represent this with a single state object with the property authenticated. You also need action creators that create actions corresponding to user login and user logout, along with the action objects themselves.


The code editor has a store, actions, and action creators set up for you. Fill in the reducer function to handle multiple authentication actions. Use a JavaScript switch statement in the reducer to respond to different action events. This is a standard pattern in writing Redux reducers. The switch statement should switch over action.type and return the appropriate authentication state.

Note: At this point, don't worry about state immutability, since it is small and simple in this example. For each action, you can return a new object — for example, {authenticated: true}. Also, don't forget to write a default case in your switch statement that returns the current state. This is important because once your app has multiple reducers, they are all run any time an action dispatch is made, even when the action isn't related to that reducer. In such a case, you want to make sure that you return the current state.

const defaultState = {
authenticated: false
};

const authReducer = (state = defaultState, action) => {
// Change code below this line
switch (action.type) {
case 'LOGIN':
return{
authenticated:true
}
break;
case 'LOGOUT':
return{
authenticated:false
}
break;
default:
return state;
}
// Change code above this line
};

const store = Redux.createStore(authReducer);

const loginUser = () => {
return {
type: 'LOGIN'
}
};

const logoutUser = () => {
return {
type: 'LOGOUT'
}
};

Redux: Use const for Action Types

A common practice when working with Redux is to assign action types as read-only constants, then reference these constants wherever they are used. You can refactor the code you're working with to write the action types as const declarations.


Declare LOGIN and LOGOUT as const values and assign them to the strings 'LOGIN' and 'LOGOUT', respectively. Then, edit the authReducer() and the action creators to reference these constants instead of string values.

Note: It's generally a convention to write constants in all uppercase, and this is standard practice in Redux as well.

// Change code below this line
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
// Change code above this line

const defaultState = {
authenticated: false
};

const authReducer = (state = defaultState, action) => {

switch (action.type) {

case LOGIN:
return {
authenticated: true
}

case LOGOUT:
return {
authenticated: false
}

default:
return state;

}

};

const store = Redux.createStore(authReducer);

const loginUser = () => {
return {
type: LOGIN
}
};

const logoutUser = () => {
return {
type: LOGOUT
}
};

Redux: Register a Store Listener

Another method you have access to on the Redux store object is store.subscribe(). This allows you to subscribe listener functions to the store, which are called whenever an action is dispatched against the store. One simple use for this method is to subscribe a function to your store that simply logs a message every time an action is received and the store is updated.


Write a callback function that increments the global variable count every time the store receives an action, and pass this function in to the store.subscribe() method. You'll see that store.dispatch() is called three times in a row, each time directly passing in an action object. Watch the console output between the action dispatches to see the updates take place.

const ADD = 'ADD';

const reducer = (state = 0, action) => {
switch(action.type) {
case ADD:
return state + 1;
default:
return state;
}
};

const store = Redux.createStore(reducer);

// Global count variable:
let count = 0;

// Change code below this line
const addOne = () => (count += 1);
store.subscribe(addOne);
// Change code above this line

store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);

Redux: Combine Multiple Reducers

When the state of your app begins to grow more complex, it may be tempting to divide state into multiple pieces. Instead, remember the first principle of Redux: all app state is held in a single state object in the store. Therefore, Redux provides reducer composition as a solution for a complex state model. You define multiple reducers to handle different pieces of your application's state, then compose these reducers together into one root reducer. The root reducer is then passed into the Redux createStore() method.

In order to let us combine multiple reducers together, Redux provides the combineReducers() method. This method accepts an object as an argument in which you define properties which associate keys to specific reducer functions. The name you give to the keys will be used by Redux as the name for the associated piece of state.

Typically, it is a good practice to create a reducer for each piece of application state when they are distinct or unique in some way. For example, in a note-taking app with user authentication, one reducer could handle authentication while another handles the text and notes that the user is submitting. For such an application, we might write the combineReducers() method like this:

const rootReducer = Redux.combineReducers({
auth: authenticationReducer,
notes: notesReducer,
});

Now, the key notes will contain all of the state associated with our notes and handled by our notesReducer. This is how multiple reducers can be composed to manage more complex application state. In this example, the state held in the Redux store would then be a single object containing auth and notes properties.


There are counterReducer() and authReducer() functions provided in the code editor, along with a Redux store. Finish writing the rootReducer() function using the Redux.combineReducers() method. Assign counterReducer to a key called count and authReducer to a key called auth.

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};

const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';

const authReducer = (state = {authenticated: false}, action) => {
switch(action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};

const rootReducer = Redux.combineReducers ({
count:counterReducer,
auth:authReducer
})// Define the root reducer here

const store = Redux.createStore(rootReducer);

Redux: Send Action Data to the Store

By now you've learned how to dispatch actions to the Redux store, but so far these actions have not contained any information other than a type. You can also send specific data along with your actions. In fact, this is very common because actions usually originate from some user interaction and tend to carry some data with them. The Redux store often needs to know about this data.


There's a basic notesReducer() and an addNoteText() action creator defined in the code editor. Finish the body of the addNoteText() function so that it returns an action object. The object should include a type property with a value of ADD_NOTE, and also a text property set to the note data that's passed into the action creator. When you call the action creator, you'll pass in specific note information that you can access for the object.

Next, finish writing the switch statement in the notesReducer(). You need to add a case that handles the addNoteText() actions. This case should be triggered whenever there is an action of type ADD_NOTE and it should return the text property on the incoming action as the new state.

The action is dispatched at the bottom of the code. Once you're finished, run the code and watch the console. That's all it takes to send action-specific data to the store and use it when you update store state.

const ADD_NOTE = 'ADD_NOTE';

const notesReducer = (state = 'Initial State', action) => {
switch(action.type) {
// Change code below this line
case ADD_NOTE:
return action.text
break;
// Change code above this line
default:
return state;
}
};

const addNoteText = (note) => {
// Change code below this line
return {
type: ADD_NOTE,
text: note
}
// Change code above this line
};

const store = Redux.createStore(notesReducer);

console.log(store.getState());
store.dispatch(addNoteText('Hello!'));
console.log(store.getState());

Redux: Use Middleware to Handle Asynchronous Actions

So far these challenges have avoided discussing asynchronous actions, but they are an unavoidable part of web development. At some point you'll need to call asynchronous endpoints in your Redux app, so how do you handle these types of requests? Redux provides middleware designed specifically for this purpose, called Redux Thunk middleware. Here's a brief description how to use this with Redux.

To include Redux Thunk middleware, you pass it as an argument to Redux.applyMiddleware(). This statement is then provided as a second optional parameter to the createStore() function. Take a look at the code at the bottom of the editor to see this. Then, to create an asynchronous action, you return a function in the action creator that takes dispatch as an argument. Within this function, you can dispatch actions and perform asynchronous requests.

In this example, an asynchronous request is simulated with a setTimeout() call. It's common to dispatch an action before initiating any asynchronous behavior so that your application state knows that some data is being requested (this state could display a loading icon, for instance). Then, once you receive the data, you dispatch another action which carries the data as a payload along with information that the action is completed.

Remember that you're passing dispatch as a parameter to this special action creator. This is what you'll use to dispatch your actions, you simply pass the action directly to dispatch and the middleware takes care of the rest.


Write both dispatches in the handleAsync() action creator. Dispatch requestingData() before the setTimeout() (the simulated API call). Then, after you receive the (pretend) data, dispatch the receivedData() action, passing in this data. Now you know how to handle asynchronous actions in Redux. Everything else continues to behave as before.

const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'

const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }

const handleAsync = () => {
return function(dispatch) {
// Dispatch request action here
dispatch(requestingData())
setTimeout(function() {
let data = {
users: ['Jeff', 'William', 'Alice']
}
// Dispatch received data action here
dispatch(receivedData(data))
}, 2500);
}
};

const defaultState = {
fetching: false,
users: []
};

const asyncDataReducer = (state = defaultState, action) => {
switch(action.type) {
case REQUESTING_DATA:
return {
fetching: true,
users: []
}
case RECEIVED_DATA:
return {
fetching: false,
users: action.users
}
default:
return state;
}
};

const store = Redux.createStore(
asyncDataReducer,
Redux.applyMiddleware(ReduxThunk.default)
);

Redux: Write a Counter with Redux

Now you've learned all the core principles of Redux! You've seen how to create actions and action creators, create a Redux store, dispatch your actions against the store, and design state updates with pure reducers. You've even seen how to manage complex state with reducer composition and handle asynchronous actions. These examples are simplistic, but these concepts are the core principles of Redux. If you understand them well, you're ready to start building your own Redux app. The next challenges cover some of the details regarding state immutability, but first, here's a review of everything you've learned so far.


In this lesson, you'll implement a simple counter with Redux from scratch. The basics are provided in the code editor, but you'll have to fill in the details! Use the names that are provided and define incAction and decAction action creators, the counterReducer(), INCREMENT and DECREMENT action types, and finally the Redux store. Once you're finished you should be able to dispatch INCREMENT or DECREMENT actions to increment or decrement the state held in the store. Good luck building your first Redux app!

const INCREMENT = 'INCREMENT'; // Define a constant for increment action types
const DECREMENT = 'DECREMENT'; // Define a constant for decrement action types

const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
}
// Define the counter reducer which will increment or decrement the state based on the action it receives

const incAction = () => {
// Change code below this line
return {
type: INCREMENT
} // Define an action creator for incrementing
}

const decAction = () => {
return {
type: DECREMENT
}// Define an action creator for decrementing
}

const store = Redux.createStore(counterReducer); // Define the Redux store here, passing in your reducers

Redux: Never Mutate State

These final challenges describe several methods of enforcing the key principle of state immutability in Redux. Immutable state means that you never modify state directly, instead, you return a new copy of state.

If you took a snapshot of the state of a Redux app over time, you would see something like state 1, state 2, state 3,state 4, ... and so on where each state may be similar to the last, but each is a distinct piece of data. This immutability, in fact, is what provides such features as time-travel debugging that you may have heard about.

Redux does not actively enforce state immutability in its store or reducers, that responsibility falls on the programmer. Fortunately, JavaScript (especially ES6) provides several useful tools you can use to enforce the immutability of your state, whether it is a string, number, array, or object. Note that strings and numbers are primitive values and are immutable by nature. In other words, 3 is always 3. You cannot change the value of the number 3. An array or object, however, is mutable. In practice, your state will probably consist of an array or object, as these are useful data structures for representing many types of information.


There is a store and reducer in the code editor for managing to-do items. Finish writing the ADD_TO_DO case in the reducer to append a new to-do to the state. There are a few ways to accomplish this with standard JavaScript or ES6. See if you can find a way to return a new array with the item from action.todo appended to the end.

const ADD_TO_DO = 'ADD_TO_DO';

// A list of strings representing tasks to do:
const todos = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code',
];

const immutableReducer = (state = todos, action) => {
switch(action.type) {
case ADD_TO_DO:
// Don't mutate state here or the tests will fail
return state.concat(action.todo);
default:
return state;
}
};

const addToDo = (todo) => {
return {
type: ADD_TO_DO,
todo
}
}

const store = Redux.createStore(immutableReducer);

Redux: Use the Spread Operator on Arrays

One solution from ES6 to help enforce state immutability in Redux is the spread operator: .... The spread operator has a variety of applications, one of which is well-suited to the previous challenge of producing a new array from an existing array. This is relatively new, but commonly used syntax. For example, if you have an array myArray and write:

let newArray = [...myArray];

newArray is now a clone of myArray. Both arrays still exist separately in memory. If you perform a mutation like newArray.push(5), myArray doesn't change. The ... effectively spreads out the values in myArray into a new array. To clone an array but add additional values in the new array, you could write [...myArray, 'new value']. This would return a new array composed of the values in myArray and the string 'new value' as the last value. The spread syntax can be used multiple times in array composition like this, but it's important to note that it only makes a shallow copy of the array. That is to say, it only provides immutable array operations for one-dimensional arrays.


Use the spread operator to return a new copy of state when a to-do is added.

const immutableReducer = (state = ['Do not mutate state!'], action) => {
switch(action.type) {
case 'ADD_TO_DO':
// Don't mutate state here or the tests will fail
return [...state,action.todo]
default:
return state;
}
};

const addToDo = (todo) => {
return {
type: 'ADD_TO_DO',
todo
}
}

const store = Redux.createStore(immutableReducer);

Redux: Remove an Item from an Array

Time to practice removing items from an array. The spread operator can be used here as well. Other useful JavaScript methods include slice() and concat().


The reducer and action creator were modified to remove an item from an array based on the index of the item. Finish writing the reducer so a new state array is returned with the item at the specific index removed.

const immutableReducer = (state = [0,1,2,3,4,5], action) => {
switch(action.type) {
case 'REMOVE_ITEM':
// Don't mutate state here or the tests will fail
return [
...state.slice(0, action.index),
...state.slice(action.index + 1, state.length)
];
default:
return state;
}
};

const removeItem = (index) => {
return {
type: 'REMOVE_ITEM',
index
}
}

const store = Redux.createStore(immutableReducer);

Redux: Copy an Object with Object.assign

The last several challenges worked with arrays, but there are ways to help enforce state immutability when state is an object, too. A useful tool for handling objects is the Object.assign() utility. Object.assign() takes a target object and source objects and maps properties from the source objects to the target object. Any matching properties are overwritten by properties in the source objects. This behavior is commonly used to make shallow copies of objects by passing an empty object as the first argument followed by the object(s) you want to copy. Here's an example:

const newObject = Object.assign({}, obj1, obj2);

This creates newObject as a new object, which contains the properties that currently exist in obj1 and obj2.


The Redux state and actions were modified to handle an object for the state. Edit the code to return a new state object for actions with type ONLINE, which set the status property to the string online. Try to use Object.assign() to complete the challenge.

const defaultState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};

const immutableReducer = (state = defaultState, action) => {
switch(action.type) {
case 'ONLINE':
// Don't mutate state here or the tests will fail
return Object.assign({},state,{status:'online'})
default:
return state;
}
};

const wakeUp = () => {
return {
type: 'ONLINE'
}
};

const store = Redux.createStore(immutableReducer);

React and Redux: Getting Started with React Redux

This series of challenges introduces how to use Redux with React. First, here's a review of some of the key principles of each technology. React is a view library that you provide with data, then it renders the view in an efficient, predictable way. Redux is a state management framework that you can use to simplify the management of your application's state. Typically, in a React Redux app, you create a single Redux store that manages the state of your entire app. Your React components subscribe to only the pieces of data in the store that are relevant to their role. Then, you dispatch actions directly from React components, which then trigger store updates.

Although React components can manage their own state locally, when you have a complex app, it's generally better to keep the app state in a single location with Redux. There are exceptions when individual components may have local state specific only to them. Finally, because Redux is not designed to work with React out of the box, you need to use the react-redux package. It provides a way for you to pass Redux state and dispatch to your React components as props.

Over the next few challenges, first, you'll create a simple React component which allows you to input new text messages. These are added to an array that's displayed in the view. This should be a nice review of what you learned in the React lessons. Next, you'll create a Redux store and actions that manage the state of the messages array. Finally, you'll use react-redux to connect the Redux store with your component, thereby extracting the local state into the Redux store.


Start with a DisplayMessages component. Add a constructor to this component and initialize it with a state that has two properties: input, that's set to an empty string, and messages, that's set to an empty array.

class DisplayMessages extends React.Component {
// Change code below this line
constructor(props) {
super(props);
this.state = {
input: "",
messages:[]
};
}

// Change code above this line
render() {
return <div />
}
};

React and Redux: Manage State Locally First

Here you'll finish creating the DisplayMessages component.


First, in the render() method, have the component render an input element, button element, and ul element. When the input element changes, it should trigger a handleChange() method. Also, the input element should render the value of input that's in the component's state. The button element should trigger a submitMessage() method when it's clicked.

Second, write these two methods. The handleChange() method should update the input with what the user is typing. The submitMessage() method should concatenate the current message (stored in input) to the messages array in local state, and clear the value of the input.

Finally, use the ul to map over the array of messages and render it to the screen as a list of li elements.

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);
}
// Add handleChange() and submitMessage() methods here
handleChange(event){
this.setState({input: event.target.value});
}

submitMessage(){
let newMessage = this.state.messages.concat(this.state.input);
console.log(newMessage);
this.setState({
input: '',
messages: newMessage
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
{ /* Render an input, button, and ul below this line */ }
<input
type="text"
value={this.state.input}
onChange={this.handleChange}
/>
<button onClick={this.submitMessage} > Enviar </button>
<ul>
{this.state.messages.map( (value) => <li>{value}</li>)}
</ul>
{ /* Change code above this line */ }
</div>
);
}
};

React and Redux: Extract State Logic to Redux

Now that you finished the React component, you need to move the logic it's performing locally in its state into Redux. This is the first step to connect the simple React app to Redux. The only functionality your app has is to add new messages from the user to an unordered list. The example is simple in order to demonstrate how React and Redux work together.


First, define an action type 'ADD' and set it to a const ADD. Next, define an action creator addMessage() which creates the action to add a message. You'll need to pass a message to this action creator and include the message in the returned action.

Then create a reducer called messageReducer() that handles the state for the messages. The initial state should equal an empty array. This reducer should add a message to the array of messages held in state, or return the current state. Finally, create your Redux store and pass it the reducer.

// define ADD, addMessage(), messageReducer(), and store here:
const ADD = "ADD";
const addMessage = message => {
return {
type: ADD,
message
};
};

// Use ES6 default paramter to give the 'previousState' parameter an initial value.
const messageReducer = (previousState = [], action) => {
// Use switch statement to lay out the reducer logic in response to different action type
switch (action.type) {
case ADD:
// Use ES6 spread operator to return a new array where the new message is added to previousState
return [...previousState, action.message];
break;

default:
// A default case to fall back on in case if the update to Redux store is not for this specific state.
return previousState;
}
};

const store = Redux.createStore(messageReducer);

React and Redux: Use Provider to Connect Redux to React

In the last challenge, you created a Redux store to handle the messages array and created an action for adding new messages. The next step is to provide React access to the Redux store and the actions it needs to dispatch updates. React Redux provides its react-redux package to help accomplish these tasks.

React Redux provides a small API with two key features: Provider and connect. Another challenge covers connect. The Provider is a wrapper component from React Redux that wraps your React app. This wrapper then allows you to access the Redux store and dispatch functions throughout your component tree. Provider takes two props, the Redux store and the child components of your app. Defining the Provider for an App component might look like this:

<Provider store={store}>
<App />
</Provider>

The code editor now shows all your Redux and React code from the past several challenges. It includes the Redux store, actions, and the DisplayMessages component. The only new piece is the AppWrapper component at the bottom. Use this top level component to render the Provider from ReactRedux, and pass the Redux store as a prop. Then render the DisplayMessages component as a child. Once you are finished, you should see your React component rendered to the page.

Note: React Redux is available as a global variable here, so you can access the Provider with dot notation. The code in the editor takes advantage of this and sets it to a constant Provider for you to use in the AppWrapper render method.

// 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
constructor(props) {
super(props);

}
render() {
return (
<Provider store={store}>
<DisplayMessages/>
</Provider>
);
}
// Change code above this line
};

React and Redux: Map State to Props

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. Once these functions are in place, you'll see how to use the React Redux connect method to connect them to your components in another challenge.

Note: Behind the scenes, React Redux uses the store.subscribe() method to implement mapStateToProps().


Create a function mapStateToProps(). This function should take state as an argument, then return an object which maps that state to specific property names. These properties will become accessible to your component via props. Since this example keeps the entire state of the app in a single array, you can pass that entire state to your component. Create a property messages in the object that's being returned, and set it to state.

const state = [];

// Change code below this line

function mapStateToProps (state){
return {
messages: state
};
};

React and Redux: Map Dispatch to Props

The mapDispatchToProps() function is used to provide specific action creators to your React components so they can dispatch actions against the Redux store. It's similar in structure to the mapStateToProps() function you wrote in the last challenge. It returns an object that maps dispatch actions to property names, which become component props. However, instead of returning a piece of state, each property returns a function that calls dispatch with an action creator and any relevant action data. You have access to this dispatch because it's passed in to mapDispatchToProps() as a parameter when you define the function, just like you passed state to mapStateToProps(). Behind the scenes, React Redux is using Redux's store.dispatch() to conduct these dispatches with mapDispatchToProps(). This is similar to how it uses store.subscribe() for components that are mapped to state.

For example, you have a loginUser() action creator that takes a username as an action payload. The object returned from mapDispatchToProps() for this action creator would look something like:

{
submitLoginUser: function(username) {
dispatch(loginUser(username));
}
}

The code editor provides an action creator called addMessage(). Write the function mapDispatchToProps() that takes dispatch as an argument, then returns an object. The object should have a property submitNewMessage set to the dispatch function, which takes a parameter for the new message to add when it dispatches addMessage().

const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};

// Change code below this line

const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message)=>{
dispatch(addMessage(message))
}
}
}

React and Redux: Connect Redux to React

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 its place.


The code editor has the mapStateToProps() and mapDispatchToProps() functions and a new React component called Presentational. Connect this component to Redux with the connect method from the ReactRedux global object, and call it immediately on the Presentational component. Assign the result to a new const called ConnectedComponent that represents the connected component. That's it, now you're connected to Redux! Try changing either of connect's arguments to null and observe the test results.

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;
// Change code below this line
const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational)

React and Redux: Connect Redux to the Messages App

Now that you understand how to use connect to connect React to Redux, you can apply what you've learned to your React component that handles messages.

In the last lesson, the component you connected to Redux was named Presentational, and this wasn't arbitrary. This term generally refers to React components that are not directly connected to Redux. They are simply responsible for the presentation of UI and do this as a function of the props they receive. By contrast, container components are connected to Redux. These are typically responsible for dispatching actions to the store and often pass store state to child components as props.


The code editor has all the code you've written in this section so far. The only change is that the React component is renamed to Presentational. Create a new component held in a constant called Container that uses connect to connect the Presentational component to Redux. Then, in the AppWrapper, render the React Redux Provider component. Pass Provider the Redux store as a prop and render Container as a child. Once everything is setup, you will see the messages app rendered to the page again.

// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
return {
type: ADD,
message: message
}
};

const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};

const store = Redux.createStore(messageReducer);

// React:
class Presentational 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>
);
}
};

// React-Redux:
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:

const Container = connect(mapStateToProps,mapDispatchToProps)(Presentational)

class AppWrapper extends React.Component {
constructor(props) {
super(props);
}
render() {
// Complete the return statement:
return (
<Provider store={store}>
<Container/>
</Provider>);
}
};

React and Redux: Extract Local State into Redux

You're almost done! 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. Currently, you have Redux connected, but you are handling the state locally within the Presentational component.


In the Presentational component, first, remove the messages property in the local state. 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. Finally, modify the render() method so that it maps over the messages received from props rather than state.

Once these changes are made, the app will continue to function the same, except Redux manages the state. This example also illustrates how a component may have local state: your component still tracks user input locally in its own state. You can see how Redux provides a useful state management framework on top of React. You achieved the same result using only React's local state at first, and this is usually possible with simple apps. However, as your apps become larger and more complex, so does your state management, and this is the problem Redux solves.

// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
return {
type: ADD,
message: message
}
};

const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};

const store = Redux.createStore(messageReducer);

// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;

// Change code below this line
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',

}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.props.submitNewMessage(this.state.input)
this.setState((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.props.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// Change code above this line

const mapStateToProps = (state) => {
return {messages: state}
};

const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message))
}
}
};

const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);

class AppWrapper extends React.Component {
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};

React and Redux: Moving Forward From Here

Congratulations! You finished the lessons on React and Redux. There's one last item worth pointing out before you move on. Typically, you won't write React apps in a code editor like this. This challenge gives you a glimpse of what the syntax looks like if you're working with npm and a file system on your own machine. The code should look similar, except for the use of import statements (these pull in all of the dependencies that have been provided for you in the challenges). The "Managing Packages with npm" section covers npm in more detail.

Finally, writing React and Redux code generally requires some configuration. This can get complicated quickly. If you are interested in experimenting on your own machine, the

Create React App comes configured and ready to go.

Alternatively, you can enable Babel as a JavaScript Preprocessor in CodePen, add React and ReactDOM as external JavaScript resources, and work there as well.


Log the message 'Now I know React and Redux!' to the console.