Redux in ReactJS

Redux lets react component states to be stored in a centralized manner. The idea is to be able to impact the state from one component and be able to reflect the change be applied to another one as they share the state through the store.

The biggest problem with bootstrapping redux with react is to keeping in mind the relations. I have found it easier to remember it in the following order.

Chronological Order:

1. Wrap main app return in Provider and pass store object to Provider as parameter. (Possible location: ./app.js)
2. Create store (Location: ./store.js)
2.1 While creating store you will need to pass your rootReducer (Location: ./reducers/index.js)
2.2 While creating rootReducer you need to combine other reducers (Example: ./reducers/postReducer.js)
2.3 While creating postReducer you need to bring in the action types. (Example: ./actions/type.js)
2.4 After creating type.js you need to create postActions.js to define actions allowed on Post component. (Example: ./actions/postActions.js)
2.5 Now that the Post actions are defined you need to connect it to the Post component. (Location: ./components/post.js)

To use redux in react you need to have reduxreact-redux and redux-thunk installed. You can install them via node package manager by typing –

npm install redux react-redux redux-thunk

The first thing needed after installing the modules is to wrap the main app return call in <Provider&gt; tag in your app.js or main app file. Make sure you import Provider from ‘react-redux’ in the app.js or your main app file. The provider requires that you give it a store object in it’s store parameter.

Step 1:

import React, {component} from 'react';
import {Provider} from 'react-redux';

import Posts from './components/posts';
import Store from './store';

class App extends React.Component{
  render(){
   return (
     <Provider store={Store}>
       <Posts/>
     </Provider>
   );
  }
} 

export default App;

You create the store object using redux’s createStore method. The method takes 3 parameters, they are 1) reducer 2) initial state 3) enhancer. You need to import createStore and applyMiddleware enhancer method from ‘redux‘ before you use them. Common approach is to save a store.js file at root and import it then pass the object as parameter. In the store.js file you should also import thunk from ‘redux-thunk‘ which is used for dispatching the action.

Step 2:

import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';

import rootReducer from './reducers';

const initialState = [];
const middleware = [thunk]

const Store = createStore(rootReducer, initialState, ...middleware); 

export default Store;

This file is the rootReducer, located at ‘./reducers/index.js’. It will combine all separate reducers into single file. combineReducers() is used for combining the reducers. It must be imported.

Step 2.1:

import { combineReducers } from 'redux';
import postReducer from './postReducer';

export default combineReducers({
  posts: postReducer
});

Step 2.2:

The following file is the ‘./reducers/postReducer.js’. It defines the initial state and the object to be returned based on the type of action.

import { FETCH_POSTS } from '../actions/types';

const initialState = {
  items: [],
  item: {}
};

export default function(state = initialState, action) {
  switch (action.type) {
    case FETCH_POSTS:
      return {
        ...state,
        items: action.payload
      };
     default:
      return state;
  }
}

Step 2.3:

Whole application can have many different action types. So, its best to store them all in a single file. For instance this file is stored at ‘./actions/types.js’;

export const FETCH_POSTS = 'FETCH_POSTS';

Step 2.4:

The following code is in ‘./actions/postActions.js’. This defines the fetchPosts() method. It needs to import action type values from ‘./actions/type.js’ so it can be dispatched as action type. The action type is a required element of the object here. Payload can be anything and not necessarily has to be payload.

import { FETCH_POSTS } from './types';

export const fetchPosts = () => dispatch => {
  fetch('https://jsonplaceholder.typicode.com/posts')
    .then(res => res.json())
    .then(posts =>
      dispatch({
        type: FETCH_POSTS,
        payload: posts
      })
    );
};

Step 2.5:

The following code is in ‘./components/Post.js’ here we connect the fetchPosts action and update the components properties from the store state values. If you notice this.props.fetchPosts() is called in componentWillMount() life-cycle method. The fetchPosts() method is injected as props when we connect the postAction with this component. The connection is set at the last line connect(mapStateToProps, { fetchPosts })(Posts);. Here the first parameter maps the state values to the component properties as per our requirement. The second parameter is taking the fetchPosts() method thats injected as property to this component.

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { fetchPosts } from '../actions/postActions';

class Posts extends Component {
  componentWillMount() {
    this.props.fetchPosts();
  }
  render() {
    const postItems = this.props.posts.map(post => (
      <div key={post.id}>
        <h3>{post.title}</h3>
        <p>{post.body}</p>
      </div>
    ));
    return (
      <div>
        <h1>Posts</h1>
        {postItems}
      </div>
    );
  }
}

Posts.propTypes = {
  fetchPosts: PropTypes.func.isRequired,
  posts: PropTypes.array.isRequired
};

const mapStateToProps = state => ({
  posts: state.posts.items
});

export default connect(mapStateToProps, { fetchPosts })(Posts);

Reference:
https://www.youtube.com/watch?v=93p3LxR9xfM
https://github.com/bradtraversy/redux_crash_course

Advertisements

ReactJS Getting Started

The following resources are some of the assorted content that proved quite helpful in learning ReactJS –

  1. Good course to getting started with ReactJS
  2. ReactJS & Redux basic
  3. React & Redux hands on video playlist
  4. Good PDF outline – React JS Notes For Professionals
  5. Project Structure Guide – https://daveceddia.com/react-project-structure/

React app on Codeanywhere

As of now there is no container available for react app only. So, you can follow these steps to get a ReactJS app running on codeanywhere.com

  • Start a blank container. I used ubuntu 14.04 based blank container.
  • Install node, npm and npm react app generator

sudo apt-get update

sudo apt-get install nodejs

sudo apt-get install npm

npm install -g create-react-app

create-react-app my-react-app

cd my-react-app

npm start

  • You will see in command prompt the development server is run and http://localhost:3000 or similar address is given for accessing it. Replace localhost with your codeanywhere.com container domain followed by the port 3000. That is the public url of your react app. The url will be similar to `http://reactjs-userx12345.codeanyapp.com/`

 

Batch compress image using google guetzli

You can use the following code written by me to run batch compression of images with google’s guetzli image compression tool. It creates a compressed folder and saves all the compressed images of the folder inside the folder. Note: Right now this only compresses image from the location that is given and not the sub-folders.

Usage:

Syntax: base [stcriptname] [relative folder location] [percentage]

Implementation: bash batch-compresssion . 84

#/usr/bin/bash

if [ ! -d "$1/compressed" ]; then
	mkdir "$1/compressed"
fi

for file in $1/*.png
do
  echo $file
  guetzli --quality $2 "$file" "$1/compressed/$file"
done

for file in $1/*.jpg
do
  echo $file
  guetzli --quality $2 "$file" "$1/compressed/$file"
done

for file in $1/*.jpeg
do
  echo $file
  guetzli --quality $2 "$file" "$1/compressed/$file"
done

Adding virtual host to bitnami magento 2 virtual machine

To setup virtual host on the bitnami virtual machine two files will have to be changed. They are htaccess.conf and httpd-vhosts.conf. These files are located at /opt/bitnami/apps/magento/conf/ folder.

First we have to update the htaccess file and add the following lines at the end –

SetEnvIf Host ^m2\.dev MAGE_RUN_CODE=base
SetEnvIf Host ^m2\.dev MAGE_RUN_TYPE=website

Here, the MAGE_RUN_CODE’s value “base” is the code for the default magento stores front-end and “^m2\.dev” is regular expression for setting domain staring with m2.dev

Once the htaccess file is updated. Now, we have to update the httpd-vhosts.conf file and add the m2.dev alias.

<VirtualHost *:80>
  ServerName yourdomain.com
  ServerAlias www.yourdomain.com m2.dev www.m2.dev
  DocumentRoot "/opt/bitnami/apps/magento/htdocs/"
  Include "/opt/bitnami/apps/magento/conf/httpd-app.conf"
</VirtualHost>

Once these two files are updated we have to restart apache server by running the following command –

sudo /opt/bitnami/ctlscript.sh restart apache

Now, we have to login to the admin using IP address one last time and go to stores > configuration > web. We have to update the “Base URL” to http://m2.dev/ and “Secure Base URL” to https://m2.dev/ and click save. After saving the user will be logged out of the system as the m2.dev domain is applied. Login back to the admin panel and now you will see notification to refresh cache. Follow the link and refresh the cache. Now you are ready to use the virtual host with this virtual machine.

Don’t forget to add the virtual host IP in the host computers /etc/hosts file.

192.168.1.108 m2.dev www.m2.dev

To keep the virtual host setup from getting reset follow the following steps.

Additional Task:
Every time the virtual machine is restarted the IP is automatically assigned as the base url of magento. This is done to ensure the application adapt to a changing IP address on each restart. But in our case this becomes a problem as on each restart we have to go and set the virtual host domain again. To stop this from happening we have to rename two files at /opt/bitnami/apps/magento/ the files that needs to be renamed are updateip and bnconfig.

mv /opt/bitnami/apps/magento/updateip /opt/bitnami/apps/magento/updateip.old
mv /opt/bitnami/apps/magento/bnconfig /opt/bitnami/apps/magento/bnconfig.old

Source: http://bit.ly/2qgRjTQ

Faster magento 2 development environment

The biggest impediment that I faced while learning to develop on Magento 2 is to setup a development environment that is not slow as a snail. After a lot of experiments I ended up selecting Bitnami’s virtual machine for Magento 2. It is the fastest of the available solutions that I have found so far.

First you have to download the virtual machine from the Bitnami website. You can download the virtual machine from this page http://bit.ly/2rQrqvo. Before you install the virtual machine you have to have Virtual Box installed in your system. Download Virtual Box from http://virtualbox.org and install before you go on to import the virtual machine. After virtual box is available on your system, double click the downloaded virtual machine appliance (bitnami-magento-2.1.6-2-linux-ubuntu-14.04-x86_64.ova) and follow the instruction on screen to add the virtual machine to your virtual box application.

After the virtual machine is imported, you should see the newly imported virtual machine on the available virtual machines list at the left side-bar. Select the machine and click the start button on top menu to start the machine.

At the beginning its going to take some time to initialize the machine. Once initialization is finished you will be asked to give the ubuntu username and password. For bitnami stack of magento 2 the username is bitnami and the password is also bitnami.

Once logged in you will be show the IP address where the virtual box is running from. For me the address was 192.168.1.108. Enter the IP on any one of you browser and you should see the magento 2 home page. To access admin panel you have to goto http://192.168.1.108/admin with username as user and password as bitnami1.

To enable ssh access use following code to activate ssh config and start ssh server.

sudo mv /etc/init/ssh.conf.back /etc/init/ssh.conf
sudo start ssh

It is possible to use virtual host instead of the IP address. Further detail on that will come in a followup post.

Add-on: Recent debian 8 version of the appliance does not work with this blog post.

To enable ssh on the debian 8 version do –


sudo rm -f /etc/ssh/sshd_not_to_be_run

sudo systemctl enable ssh
sudo systemctl start ssh

htaccess code for redirecting get request from index.php to index.html for temporary landing page

Sometimes for marketing purposes if you have a static landing page index.html and in the background there is another application running via index.php you can redirect all get request to the index.php to be redirected to the index.html using the following code. But if the request is not GET request or if the request has query parameters then it will not apply this rule and take user to the index.php instead.

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} ^$
RewriteRule index\.php$ http://companydomain.com/index.html [R=301,L]
</IfModule>