QR Code generator with React.js and Redux

Getting to understand Redux

Victor Le Nerd
6 min readJun 17, 2016

Am a big fan of Angularjs, usually there’s always a high chance that I would start my next side project with Angular, however have been reading alot about unidirectional data flow and the various implementations i.e Flux, Redux and recently Angular 2.

I experimented with Flux for a while but I couldn’t use it for any side projects mainly because of how much time that goes into starting a new project with fluxxor, and to be honest I only tried it once and I didn’t go back to it.

On the other hand Redux is quite simple to use. On the redux website it defines redux as A predictable state container for JavaScript apps. I didn’t quite understand it at first until I used it alongside React on project.

The Idea

Simply generating qr-codes for a product, such that users can decide to print the qr-codes and place them on all the copies of the product, the product could be anything from album covers to medicine pack e.t.c. The objective is to help prevent piracy of the product.

The App UI

This is where the user will create the product with the product name, author, email and number of copies of the product.
The generated qr-codes based on the number of product copies.

The Code

I won’t go into too much details on the React code. The code is pretty much self explanatory if you have experience with React.

Based on the UI there are two layouts, the first holds the table, and form while the other displays the qr-codes.

The Layouts

The first layout holds the table and the form components.

import React from ‘react’;
import ProductFormContainer from ‘../containers/product-form-container’;
import ProductListContainer from ‘../containers/product-list-container’;
const Home = React.createClass({
render: function() {
return (
<div className=”col-lg-12">
<div className=”container”>
<div className=”col-lg-8">
<div className=”panel panel-default”>
<div className=”panel-heading”>Products</div>
<ProductListContainer></ProductListContainer>
</div>
</div>
<div className=”col-lg-4">
<ProductFormContainer></ProductFormContainer>
</div>
</div>
</div>
);
}
});
export default Home;

The second layout displays the qr-codes

import React from ‘react’;
import ProductListCodeContainer from ‘../containers/product-list-qrcodes-container’;
const ProductQRCodes = React.createClass({
render: function() {
return (
<div className=”col-lg-12">
<div className=”container”>
<ProductListCodeContainer productId={this.props.params.id} />
</div>
</div>
);
}
});
export default ProductQRCodes;

The Containers

Containers are important because they help separate concerns on the react components, the containers stay aware of the data, things like fetching of data should be done in the container.

import React from ‘react’;
import { connect } from ‘react-redux’;
import ProductForm from ‘../components/product-form’;
const ProductFormContainer = connect()(ProductForm);export default ProductFormContainer;

The connect()() function is used by redux to link the container with the state.

import React from ‘react’;
import { connect } from ‘react-redux’;
import ProductCodeList from ‘../components/product-code-list’;
const mapStateToProps = (state, ownProps) => {
return {
productId: ownProps.productId,
products: state
}
}
const ProductListCodeContainer = connect(mapStateToProps)(ProductCodeList);export default ProductListCodeContainer;

The connect()() function pass down the url params.

import React from ‘react’;
import { connect } from ‘react-redux’;
import ProductCodeList from ‘../components/product-code-list’;
const mapStateToProps = (state, ownProps) => {
return {
productId: ownProps.productId,
products: state
}
}
const ProductListCodeContainer = connect(mapStateToProps)(ProductCodeList);export default ProductListCodeContainer;

The connect()() function pass down the url param and state for the second layout.

The Form Component

Note that these are mainly presentational components.

import React from ‘react’;import addProduct from ‘../action’;
import IDGenerator from ‘../IDGenerator’;
const ProductForm = ({ dispatch }) => {

let nameInput, authorInput, emailInput, copiesInput;

let submitForm = (e) => {
e.preventDefault();
let product = {
name: nameInput.value,
author: authorInput.value,
email: emailInput.value,
copies: copiesInput.value,
codes: []
}
for (let i=0; i<product.copies; i++) {
product.codes.push(IDGenerator(6));
}
dispatch(addProduct(product));
}
return (
<form name=”addform” onSubmit={submitForm}>
<input className=”form-control”
type=”text”
ref={ node => { nameInput = node; }}
placeholder=”Product Name”
required=”true”
name=”name” />
<br />
<input className=”form-control”
type=”text”
ref={ node => { authorInput = node; }}
placeholder=”Author Name”
required=”true”
name=”author” />
<br />
<input className=”form-control”
type=”email”
ref={ node => { emailInput = node; }}
placeholder=”Author Email”
required=”true” name=”email” />
<br />
<input className=”form-control”
type=”number”
ref={ node => { copiesInput = node; }}
required=”true”
min=”1"
max=”999"
name=”copies” />
<br />
<input type=”submit”
className=”btn btn-default”
value=”Add Product” />
</form>
)
}
export default ProductForm

The Table Component

import React from ‘react’;
import Product from ‘./product’;
const ProductList = ({ products }) => (
<table className=”table”>
<thead>
<tr className=”tr”>
<td className=”td”>#</td>
<td className=”td”>Name</td>
<td className=”td”>Author Name</td>
<td className=”td”>Author Email</td>
<td className=”td”>Copies</td>
</tr>
</thead>
<tbody>
{
products.map((product, index) => {
return
<Product key={index} index={index} product={product} />
})
}
</tbody>
</table>
)
export default ProductList;

Table Item Component

import React from ‘react’;
import { browserHistory } from ‘react-router’;
const Product = React.createClass({
openQRcodes:function () {
browserHistory.push(`/product/${this.props.index}`);
},
render: function () {
let product = this.props.product;
let index = this.props.index;
return (
<tr className=”tr” onClick={this.openQRcodes}>
<td className=”td”>{index+1}</td>
<td className=”td”>{product.name}</td>
<td className=”td”>{product.author}</td>
<td className=”td”>{product.email}</td>
<td className=”td”>{product.copies}</td>
</tr>
);
}
});
export default Product;

List QR-Codes component

import React from ‘react’;
import ProductCode from ‘./product-code’;
const ProductCodeList = ({ products, productId }) => (
<div className=”col-lg-12">
{
products[productId].codes.map((code, index) => {
return <ProductCode key={index} code={code} />
})
}
</div>
)
export default ProductCodeList;

QR-Code Component

import React from ‘react’;const ProductCode = React.createClass({
componentDidMount: function () {
new QRCode(this._node, { width:100, height:100, text: this.props.code })
},
render: function () {
return (<div ref={node => { this._node = node } } className=”product”></div>)
}
});
export default ProductCode;

I used a third party library to generate the qr-code.

Router

import React from ‘react’;
import { Router, Route, browserHistory } from ‘react-router’;
// Pages
import Home from ‘./layouts/home’;
import ProductQRCodes from ‘./layouts/product-qrcodes’;
export default (
<Router history={browserHistory}>
<Route path=”/” component={Home} />
<Route path=”product/:id” component={ProductQRCodes} />
</Router>
);

App.js

import React from ‘react’;
import ReactDOM from ‘react-dom’;
import { Provider } from ‘react-redux’;
import store from ‘./store’;
import Router from ‘./router’;
// Now we can attach the router to the ‘root’ element like this:
ReactDOM.render(
<Provider store={store}>
{Router}
</Provider>,
document.getElementById(‘root’)
);

Why Redux?

The Problem With MVC

It’s important to keep track of the state of the application, with Angular v.1 the state of the application is nested within controllers Angular v.1 also exposes the state of the application with the scope this means that the state of the application can be altered by other controllers, directives, or services hence makes it difficult to predict the state of the app overtime has the codebase grows, this problem results from the architecture of Angular v.1. which is MVC.

How Redux Is Different

Redux stores the state of the application in a central location, the state is not distributed based on the number of components in the application but rather all components refer to a single location for the state of the application.

Secondly, the state is read only, the state cannot be changed by the various components that make up the application.

And finally the state of the application can only be changed by a pure function.

And this is how i implemented the architecture in my side projects.

Action. An action is a command that is used to change the state.

const addProduct = (product) => {
return {
type: ‘ADD_PRODUCT’,
product
}
}
export default addProduct;

Reducer. Reducer is a pure function that accepts an action and collection as parameters, the reducer will reduce collection down to a single value.

const Products = (state = [], action) => {

switch (action.type) {
case ‘ADD_PRODUCT’:
return [
…state,
{
name: action.product.name,
author: action.product.author,
email: action.product.email,
copies: action.product.copies,
codes: action.product.codes
}
]
default:
return state
}
return state;
}
export default Products;

Store. The store holds the application state

import { createStore } from ‘redux’;
import Products from ‘./reducer’;
const store = createStore(Products);
export default store;

Conclusion

One advantage redux adds is the ability to implement undo and redo features on the app. Am going be using redux a lot more often. Am also going write an article on redux and Angular.v2.

Don’t forget to click the like button if you like this post…Thanks.

--

--

Victor Le Nerd

I love to build beautiful digital products for startups and other enterprises. I write about coding, entrepreneurship and poetry.https://github.com/victorlenerd