By Kevin Hou
6 minute read
Redux is a way of keeping all states consolidated in one master, global javascript object (called a Redux tree) and accessed using a store. In order to understand the basics, it is important to recognize the difference between pure and impure functions. Pure functions have no observable side effects on the database or server; in fact, the return values depend solely on the values of the arguments. If you put in the same arguments, you should get the exact same output every time. An impure function does not do this. It may modify the DOM or change a database. Sidenote: You can deep freeze your input variable to ensure that your function is pure
State changes will result in a callback called the reducer function. It is imperative that this function be a pure function and it must return a new object, not a reference to the input. In most cases, you do not need to recreate the entire object. You can keep the references to the objects properties, but the master reference must be different — just change the variable(s) that need to be changed. Sidenote: You can use an expect(...).toEqual(...) to debug in Javascript. They act like Java assertion statements
It is also important that the reducer functions consider all possible inputs of an action. By convention, undefined inputs will return the initial statement. A reducer accepts a state and an action. In the following implementation, here are a few important redux API's to keep in mind:
There are a few main pieces when implementing Redux with ReactJS. It centers around the 'store' or class that controls the global instance variables. In this explanation, we will use a simple counter as the example store. It simply tracks a number and has two API endpoint actions: increment by one, decrement by one.
First, we must write out a simple React component so that we have something to connect our redux store to. Install the React NPM package so that we can use the framework.
1$ npm install --save react 2
Now we must create the controller (written in ES6).
1import React from "react"; // Import the React NPM package
2
3// React Component
4export default class Counter extends React.Component {
5 render() { // Rendering function
6 return (
7 <div>
8 <h1>Counter: {this.props.value}</h1>
9 <button onClick={this.props.onIncrement}>Add</button>
10 <button onClick={this.props.onDecrement}>Subtract</button>
11 </div>
12 );
13 }
14};
15
As we will see later, we will pass in the value of the counter and two functions (increment and decrement) from the store we will eventually create.
To set up our store, we must first install redux and use the --save option to include it in our package.json:
1$ npm install --save redux 2
Now we must write the code for the store itself. The store is actually an object type generated running the createStore() function in the redux package on a class. The code is included below. Notice how the function accepts an action and does not actually modify the state — it is a pure function. In this example, the state is the value of the counter and it is initially set to 0. It is written in ES6.
1// counterStore.js
2import { createStore } from "redux"; // Get the createStore export
3
4// Setup the Redux instance
5const counter = (state = 0, action) => { // Initially at 0
6 console.log(action.type); // Feedback
7 switch (action.type) { // Use a switch to determine what action to take
8 case 'INCREMENT':
9 return state + 1;
10 case 'DECREMENT':
11 return state - 1;
12 default:
13 return state;
14 }
15}
16
17// Use the function above as the callback and generate a store type
18const store = createStore(counter);
19
20// Export the store so that it can used in other files
21export default store;
22
Next, we will link the store with our React class so that it can dynamically update the frontend. We include the store as a local variable in the file using the require() statement:
1import store from "./counterStore"; 2
Side Note: In ES6, you can include exports from other JS files using brackets or no brackets. If there is a default export statement in the file, you do not have to use brackets and any import statement will simply load the default export as the variable name you specified. If you do not have a default export, want to access a number of different exports, or want to access a specific export, you must use the brackets and use the exact name of the export. (citation)
Now we will create a rendering function, using ReactDOM to inject our HTML into the DOM.
1require("./style.css"); // Include CSS stylesheet
2import React from "react"; // Include React
3import ReactDom from "react-dom"; // ReactDOM to load our component into the page
4
5import Counter from "./content"; // Get our Counter React component
6import store from "./counterStore"; // Don't use brackets because it is the default export
7
8// Render function so that we can set it as a callback every time the store variable instance is modified
9// We are also passing in the counter value and increment/decrement functions
10// The component is rendered in the element with the id: 'main'
11const render = () => {
12 ReactDom.render(
13 <Counter
14 value = { store.getState() }
15 onIncrement = { () =>
16 store.dispatch({ type: 'INCREMENT' })
17 }
18 onDecrement = { () =>
19 store.dispatch({ type: 'DECREMENT' })
20 }
21 />,
22 document.getElementById('main')
23 )
24}
25
26// Subscribe to the live updates
27// This will run the function 'render' every time the store encounters a change
28store.subscribe(render);
29
30// Call it once manually to push it into the DOM because it will only render on a change
31// If we don't manually call it, there can be no changes to put it in the dom in the first place
32render();
33
Hope you were able to follow this brief tutorial! It is loosely based on the free course on egghead.io on Redux and React. The full code can be found on my GitHub.