0% found this document useful (0 votes)
25 views15 pages

Redux

This document provides a comprehensive guide on using Redux for state management in JavaScript applications. It covers creating a Redux store, defining actions and action creators, dispatching actions, handling actions in reducers, combining multiple reducers, and managing asynchronous actions with middleware. The document also includes practical examples and code snippets to illustrate each concept.

Uploaded by

AKINDUN JOHN
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views15 pages

Redux

This document provides a comprehensive guide on using Redux for state management in JavaScript applications. It covers creating a Redux store, defining actions and action creators, dispatching actions, handling actions in reducers, combining multiple reducers, and managing asynchronous actions with middleware. The document also includes practical examples and code snippets to illustrate each concept.

Uploaded by

AKINDUN JOHN
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 15

REDUX FROM FREECODECAMP DOCUMENTATION

Create a Redux Store


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)

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.
const store = Redux.createStore(
(state = 5) => state
);
// Change code below this line
const currentState = store.getState()

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.

// Define an action here:


const action = {
'type':'LOGIN'
}

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.
const action = {
type: 'LOGIN'
}
// Define an action creator here:
function actionCreator(){
return action
}
console.log(actionCreator())

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 type of
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' });

const store = Redux.createStore(


(state = {login: false}) => state
);

const loginAction = () => {


return {
type: 'LOGIN'
}
};

// Dispatch the action here:


console.log(store.dispatch(loginAction()))

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

if (action.type === "LOGIN") {


return {
login: true
};
} else {
return state;
}
// Change code above this line
};

const store = Redux.createStore(reducer);

const loginAction = () => {


return {
type: 'LOGIN'
}
};
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
};

case "LOGOUT":
return {
authenticated: false
};

default:
return defaultState;
}
// Change code above this line
};
const store = Redux.createStore(authReducer);

const loginUser = () => {


return {
type: 'LOGIN'
}
};

const logoutUser = () => {


return {
type: 'LOGOUT'
}
};

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 increment = () =>{
return count++
}
store.subscribe(increment)
// 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);

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}, ac


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

const rootReducer = Redux.combineReducers({


auth: authReducer,
count: counterReducer
});// Define the root reducer here

const store = Redux.createStore(rootReducer);


console.log(store)

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;

// 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());

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)
);

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;
break;
case DECREMENT: return state-1;
break;
default: return state;
}
}; // Define the counter reducer which will increment o
r decrement the state based on the action it receives

const incAction = ()=>{


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); // Def


ine the Redux store here, passing in your reducers
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);

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
let arr = [...state,action.todo]
return arr;
default:
return state;
}
};

const addToDo = (todo) => {


return {
type: 'ADD_TO_DO',
todo
}
}

const store = Redux.createStore(immutableReducer);

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);

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":
// to enforce state immutability, return a new st
ate object using Object.assign() method
return Object.assign({}, state, { status: "online
" });
default:
return state;
}
};

const wakeUp = () => {


return {
type: "ONLINE"
};
};

const store = Redux.createStore(immutableReducer);

You might also like