- Notifications
You must be signed in to change notification settings - Fork0
ShMcK/coderoad-redux-js
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
ACodeRoad tutorial for learning Redux.
CodeRoad is an open-sourced interactive tutorial platform for the Atom Editor. Learn more atCodeRoad.io.
install the tutorial package
npm install --save coderoad-redux-js
install and run theatom-coderoad plugin
Welcome! In this tutorial series we'll be exploring Redux, a tool for predictably managing state in your app.
We will be making a "Worst Pokemon" voting app. For the app, we'll need to setup some build tools.
Running> npm run setup
will do the following:
- Install package dev dependencies
- Create an output directory called "dist"
- Install "concurrently" & "browser-sync" globally
- Run our app in the browser
You'll find this "setup" script located in yourpackage.json.
We'll be installing several NPM packages from terminal. You may consider installing a plugin for adding a terminal inside your editor, such as"platformio-ide-terminal".
In Redux, thestore is the boss. Think of thestore as holding the "single source of truth" of your application data.
Once created, thestore has several helpful methods:
getState
to read the current data of your app.dispatch
to trigger actions. We'll look at actions more later.subscribe
to listen for state changes
Let's get started by settings up thestore for your Redux app. We will be working in "src/index.js".
Anaction is a named event that can trigger a change in your application data.
Actions are often broken into three parts to make your code more readable.
Anaction includes a named "type".
constaction={type:'ACTION_NAME'};
Actions may also include other possible params needed to transform that data.
constgetItem={type:'GET_ITEM',clientId:42,payload:{id:12}};
Normal params passed in are often put inside of apayload
object. This is part of a standard calledFlux Standard Action. Other common fields includeerror
&meta
.
Anaction creator is a functions that creates an action.
constactionName=()=>({type:'ACTION_NAME'});
Action creators make it easy to pass params into an action.
constgetItem=(clientId,id)=>({type:'GET_ITEM',clientId:42,payload:{id:12}});
Often, the action name is also extracted as anaction type. This is helpful for readability and to catch action name typos. Additionally, most editors will auto-complete your action types from the variable name.
constACTION_NAME='ACTION_NAME';constGET_ITEM='GET_ITEM';constaction=()=>({type:ACTION_NAME});constgetItem=(id)=>({type:GET_ITEM,payload:{ id}});
Let's write an action for voting up your choice of worst pokemon.
Areducer is what handles the actual data transformation triggered by an action.
In it's simplest form, areducer is just a function with the currentstate and currentaction passed in.
constreducer=(state,action)=>{console.log(state);returnstate;};
We can handle different actions by matching on the action type. If no matches are found, we just return the original state.
constACTION_NAME='ACTION_NAME';constreducer=(state,action)=>{switch(action.type){// match on action.type === ACTION_NAMEcaseACTION_NAME:state=42;// return new state after transformationreturnstate;default:returnstate;}};
Our reducer is passed in as the first param when we create ourstore.
Redux totes itself as a "predictable" state container. This predictability is the product of some helpful restrictions that force us to write better code.
One such guideline: reducers must be pure functions.
When an action passes through a reducer, it should not "mutate" the data, but rather return a new state altogether.
case ADD_TO_ARRAY:/* bad */state.push(42);// push mutates the statereturnstate;
If multiple actions were pushing into the state, the functions are no longerpure and thus no longer fully predictable.
By returning an entirely new array, we can be sure that our state will bepure and thus predictable.
case ADD_TO_ARRAY:/* good */returnstate.concat(42);// returns a new array, with 42 on the end
Let's give writing pure reducers a try as we implement ourVOTE_UP
action.
In Redux, we are not limited to writing a long, single reducer. UsingcombineReducers
allows us to create modular, composable reducers.
As our state is an object, for example:
{pokemon:[ ...],users:[ ...]}
We can create a reducer to handle data transformations for each key in our state.
{pokemon:pokemonReducer,users:usersReducer}
As our app grows, we can now think of the data in smaller chunks.
Let's try refactoring our app to usecombineReducers
.
Our "index.js" file is getting a little long. Of course, our app will be more maintainable if we can divide it across different, well structured files.
There are different ways of structuring your app:
- store.js
- action-types.js
- action-creators.js
- reducers.js
- store.js
- reducers.js
- modules
- pokemon
- index.js
- pokemon
- store
- reducers.js
- modules
- pokemon
- actions.js
- reducer.js
- action-types.js
- pokemon
For simplicity in this example, we'll try putting our files together by function.
We still haven't worked with one of the most powerful features of Redux:middleware.
Middleware is triggered on each action.
1. Dispatch(action) -> 2. Middleware(state, action) -> 3. Reducer(state, action) -> 4. state
Middleware is created with thestore
. In it's most basic form, middleware can look like the function below:
conststore=>next=>action=>{// do something magical here returnnext(action);// returns result of reducer called with action}
Let's try out the power of middleware with "redux-logger".
Notice how the votes remain out of order. Let's create a sorting action for putting the highest votes at the top.
For this, we'll use a sorting function. A sorting function takes two values, and returns either:
1
: move ahead-1
: move behind0
: no change
See an example for sorting votes below:
functionsortByVotes(a,b){switch(true){casea.votes<b.votes:return1;casea.votes>b.votes:return-1;default:return0;}}
Let's setup aSORT_BY_POPULARITY
action to be called after each vote.
As we've seen in the previous steps, thunks sound more complicated than they really are. A thunk is just a function that returns a function.
Inside of middleware, we can determine if an action is returning a function.
conststore=>next=>action=>{if(typeofaction==='function'){// it's a thunk!}returnnext(action);}
If it is a thunk, we can pass in two helpful params:
store.dispatch
store.getState
As we'll see,dispatch
alone can allow us to create async or multiple actions.
About
CodeRoad tutorial for learning Redux
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.