First install create-react-app https://github.com/facebook/create-react-app
> npm install -g create-react-app
> create-react-app react-redux-kss
To understand what is Redux we must first understand what is the state.
A stateful React component is a Javascript ES6 class. [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes]
In a React component the state holds up data and the component might render such data.
The state could also change in response to actions and events: to update the local component's state use setState.
A typical JavaScript application is full of states.
State is everywhere in JavaScript.
As you can see even the simplest JavaScript application has a state.
Here are some examples of state:
- what the user sees (data)
- what data are we fetching
- what URL are we showing to the user
- what items are selected inside the page
- are there errors in the applications? That's state too
As long as the application remains small, we can get by with keeping the state within a parent React component.
Then things will become tricky, when application became complex.
Also frontend shouldn't know about the business logic. Ever.
So one of the alternatives for managing the state of a React component: Redux
Redux solves a problem that might not be clear in the beginning : it helps giving each React component the exact piece of state it needs.
Redux holds up the state within a single location.
Also with Redux the logic for fetching and managing the state lives outside React.
- Redux is framework agnostic. Learn it once, use it everywhere (Vue JS, Angular)
Redux is just a library among the others (like flux, mobx etc), which might of-course disappear in future :) But the patterns will stick forever.
consider using Redux when:
- multiple React components needs to access the same state but do not have any parent/child relationship
- you start to feel awkward passing down the state to multiple components with props
Be aware that Redux is not useful for smaller apps. It really shines in bigger ones.
Dan Abramov says "Flux libraries are like glasses: you'll know when you need them."
Dan Abramov => Working on @reactjs. Co-author of Redux and Create React App.
You Might Not Need Redux [https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367]
The store in Redux is like the human brain: it's kind of magic.
The Redux store is fundamental : the state of the whole application lives inside the store.
So to start playing with Redux we should create a store for wrapping up the state.
> yarn add redux
Create a directory for the store:
> mkdir -p src/store
Create a new file named index.js in src/store and finally initialize the store:
- import{ createStore } from "redux";
- import reducer from "./reducer";
- const store = createStore(reducer);
- exportdefault store;
createStore is the function for creating the Redux store. [https://redux.js.org/api-reference/createstore]
createStore takes a reducer as the first argument, [rootReducer in our case, which we will create in a while].
We may also pass an initial state to createStore. But most of the times we don't have to. Passing an initial state is useful for server side rendering. Anyway, the state comes from reducers.
Problem : Reducer returned undefined during initialization [ https://stackoverflow.com/questions/36619093/why-do-i-get-reducer-returned-undefined-during-initialization-despite-pr ]
In Redux reducers produce the state .
A reducer is just a Javascript function. A reducer takes two parameters : the current state and an action (more about actions soon).
Third Principle of Redux says that the state is immutable and cannot change in place.
Note: Three principle of Redux [https://redux.js.org/introduction/three-principles]
This is why the reducer must be pure. [A pure function is one that returns the exact same output for the given input.]
In plain React the local state changes in place with setState. In Redux you cannot do that.
Lets create a file inside src/store directory called reducer.js
- const initialState ={
- articles:[]
- };
- const reducer =(state = initialState, action)=> state;
- exportdefault reducer;
In our example we created a simple reducer taking the initial state as the first parameter. As a second parameter we had provided action. As of now the reducer will do nothing than returning the initial state.
Redux reducers are without doubt the most important concept in Redux. Reducers produce the state of the application.
But how does a reducer know when to produce the next state?
The second principle of Redux says the only way to change the state is by sending a signal to the store. This signal is an action. " Dispatching an action" is the process of sending out a signal.
Redux actions are nothing more than Javascript objects. This is what an action looks like:
- {
- type:'ADD_ARTICLE',
- payload:{ name:'React Redux KSS, id:1}
- }
Every action needs a type property for describing how the state should change.
We can specify a payload as well. In the above example the payload is a new article. A reducer will add the article to the current state later.
It is a best practice to wrap every action within a function. Such function is an action creator.
Let's put everything together by creating a simple Redux action.
- // src/store/js
- exportconst addArticle = article =>({ type:"ADD_ARTICLE", payload: article });
So, the type property is nothing more than a string.
The reducer will use that string to determine how to calculate the next state.
Since strings are prone to typos and duplicates it's better to have action types declared as constants.
This approach helps avoiding errors that will be difficult to debug.
- // src/store/types.js
- exportconst ADD_ARTICLE ="ADD_ARTICLE";
Now, update the action to use action types:
- // src/store/actions.js
- import{ ADD_ARTICLE } from "./types";
- exportconst addArticle = article =>({ type: ADD_ARTICLE, payload: article });
reducer calculates the next state depending on the action type. Moreover, it should return at least the initial state when no action type matches.
When the action type matches a case clause the reducer calculates the next state and returns a new object.
- import{ ADD_ARTICLE } from "./types";
- const initialState ={
- articles:[]
- };
- const reducer =(state = initialState, action)=>{
- switch (action.type){
-
**case** ADD\_ARTICLE:
-
state.articles.push(action.payload);
-
**return** state;
- default:
-
**return** state;
- }
- };
- exportdefault reducer;
Although it's valid code the above reducer breaks the main Redux principle: immutability.
Array.prototype.push is an impure function: it alters the original array.
Making our reducer compliant is easy. Using Array.prototype.concat in place of Array.prototype.push is enough to keep the initial array immutable:
- import{ ADD_ARTICLE } from "./types";
- const initialState ={
- articles:[]
- };
- const reducer =(state = initialState, action)=>{
- switch (action.type){
-
**case** ADD\_ARTICLE:
-
**return** {...state, articles: state.articles.concat(action.payload)};
- default:
-
**return** state;
- }
- };
- exportdefault reducer;
With the spread operator we can make our reducer even better:
return {...state, articles:[...state.articles, action.payload]};
for avoiding mutations in Redux :
- Using concat(), slice(), and …spread for arrays
- Using Object.assign() and …spread for objects
Redux on its own is framework agnostic. We can use it with vanilla Javascript. Or with Angular. Or with React. Or with VueJS. There are bindings for joining together Redux with your favorite framework/library or we can use redux's own functionalities.
For React there is react-redux , a bindings of reactJs and Redux. It's a small library for connecting Redux and React in an efficient way.
> yarn add react-redux
The most important method we'll work with react-redux is connect()
What does react-redux's connect do? Unsurprisingly it connects a React component with the Redux store.
Other fundamental things to know are:
- the mapStateToProps function
- the mapDispatchToProps function
What does mapStateToProps do in react-redux?
mapStateToProps does exactly what its name suggests: it connects a part of the Redux state to the props of a React component. By doing so a connected React component will have access to the exact part of the store it needs. (So read it as mapStoreToProps )
What does mapDispatchToProps do in react-redux?
mapDispatchToProps does something similar, but for actions. mapDispatchToProps connects Redux actions to React props. This way a connected React component will be able to dispatch actions.
We saw that mapStateToProps connects a portion of the Redux state to the props of a React component. You may wonder: is this enough for connecting Redux with React? No, it's not.
To start off connecting Redux with React we're going to use Provider.
Provider is an high order component coming from react-redux.
Using layman's terms, Provider wraps up your React application and makes it aware of the entire Redux's store.
Open up src/index.js, wipe out everything and update the file with the following code:
- import React from "react";
- import{ render } from "react-dom";
- import{ Provider } from "react-redux";
- import store from "./store/index";
- import App from "./components/App";
- render(
- <Provider store={store}>
- <App />
- </Provider>,
- document.getElementById("root")
- );
Provider wraps up your entire React application. Moreover it gets the store as a prop.
Now, create the App component that import a List component and render itself.
- // src/components/App.js
- import React from "react";
- import List from "./List";
- const App =()=>(
- <div className="row mt-5">
- <div className="col-md-4 offset-md-1">
- <h2>Articles</h2>
-
<List />
- </div>
- </div>
- );
- exportdefault App;
our new component, List, will interact with the Redux store.
- // src/components/List.js
- import React from "react";
- import{ connect } from "react-redux";
- const mapStateToProps = state =>{
- return { articles: state.articles};
- };
- const ConnectedList =({ articles })=>(
- <ul className="list-group list-group-flush">
- {articles.map(article =>(
-
<li className="list-group-item" key={article.id}>
-
{article.title}
-
</li>
- ))}
- </ul>
- );
- const List = connect(mapStateToProps)(ConnectedList);
- exportdefault List;
The Form component we're going to create a stateful component.
A stateful component in React is a component carrying its own local state
Create a new file named Form.js inside src/components. It should look like the following:
- // src/components/Form.js
- import React,{ Component } from "react";
- import{ connect } from "react-redux";
- import uuidv1 from "uuid";
- import{ addArticle } from "../store/actions";
- const mapDispatchToProps = dispatch =>{
- return {
- addArticle: article => dispatch(addArticle(article))
- };
- };
- class ConnectedForm extends Component {
- constructor(){
- super();
-
**this**.state={
-
title:""
- };
-
**this**.handleChange= **this**.handleChange.bind( **this** );
-
**this**.handleSubmit= **this**.handleSubmit.bind( **this** );
- }
- handleChange(event){
-
**this**.setState({[event.target.id]: event.target.value});
- }
- handleSubmit(event){
- event.preventDefault();
- const{ title }= this.state;
- const id = uuidv1();
-
**this**.props.addArticle({ title, id });
-
**this**.setState({ title:""});
- }
- render(){
- const{ title }= this.state;
-
**return** (
-
<form onSubmit={ **this**.handleSubmit}>
-
<div className="form-group">
-
<label htmlFor="title">Title</label>
-
<input
-
type="text"
-
className="form-control"
-
id="title"
-
value={title}
-
onChange={ **this**.handleChange}
-
/>
-
</div>
-
<button type="submit" className="btn btn-success btn-lg">
-
SAVE
-
</button>
-
</form>
- );
- }
- }
- const Form = connect(null, mapDispatchToProps)(ConnectedForm);
- exportdefault Form;
Update App to include the Form component:
- import React from "react";
- import List from "./List";
- import Form from "./Form";
- const App =()=>(
- <div className="row mt-5">
- <div className="col-md-4 offset-md-1">
-
<h2>Articles</h2>
-
<List />
- </div>
- <div className="col-md-4 offset-md-1">
-
<h2>Add a new article</h2>
-
<Form />
- </div>
- </div>
- );
- exportdefault App;
Redux protip : the reducer will grow as our app will become bigger. We can split a big reducer into separate functions and combine them with combineReducers [https://redux.js.org/docs/api/combineReducers.html]
Also, we are currently following " re-ducks" structure to manage store in larger application ( https://github.com/alexnm/re-ducks)
Scaling your Redux App with ducks
https://medium.freecodecamp.org/scaling-your-redux-app-with-ducks-6115955638be#.4ppptx7oq
We have everything explained in Redux's documentation