React logo

React 16.6 new features: contextType, lazy, memo

In Oct 24, 2018, React 16.6.0 was published to npm. It brings a lot of exciting new features, including contextType, React.lazy() and React.memo(). We will discuss in depths of what they are and how will they change your React ecosystem.

In this blog post, I am going to assume that you already have certain experience with React, and have been working with React for a little while (mainly for understanding the tremendous advantages that the new features will bring us.

contextType

In React 16.3, Facebook released context API officially, shaking the foundation of state management systems like redux, mobx. In React 16.6, context API is further improved by introducing static contextType.

const SampleContext = React.createContext();

class WithContextAPIComponent extends React.Component {
  render() {
    return (
      <SampleContext.Consumer>
        {(value) => (<div>value.name</div>)}
      </SampleContext.Consumer>
    );
  }
}

class WithContextTypeComponent extends React.Component {
  static contextType = SampleContext;
  
  render() {
    const value = this.context;
    
    return (<div>value.name</div>);
  }
}

// usage
<SampleContext.Provider value={{ name: 'Tuan Nguyen' }}>
  <WithContextAPIComponent />
  <WithContextTypeComponent />
</SampleContext.Provider>

With the sample above, we can see that with contextType, we bound the context value to the component itself, which simplifies one layer of nested component to be rendered. This suddenly looks like it is so much similar to how we use redux store!!!

 

React.memo()

This is very simple. It enables the same functionality of React.PureComponent for functional components.

const MyComponent = React.memo(function MyComponent(props) {
  /* only rerenders if props change */
});

Previously functional components will re-render every time its props change, regardless of value.

With React.memo(), now functional components will shallowly check whether props values have changed, and decide to re-render.

React.lazy()

In my opinion, this is the most outstanding feature that React 16.6 offers.

Short description: it allows React components to only be downloaded from server on demand.

import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));
function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OtherComponent />
    </Suspense>
  );
}

In the example, MyComponent will download OtherComponent only when it is needed.

This is very useful in tabular structure, where most users do not browse every tab content.

 

For more information, let’s check out the changelog at https://reactjs.org/blog/2018/10/23/react-v-16-6.html

Write automated script with Tampermonkey and Webpack

Recently I was pulled into an old web-based game called Ghost Trapper. The gist of it is, after a period of time, I can click on a button to trap a ghost, then a count down appears and I have to wait for the count down to stop until I can hunt ghosts again.

Then it hit me, why don’t I automate the process so that I don’t have to spend so much time to play anymore.

Here’s a screenshot of the game.

Ghost trapper screen image

Note the top left count down. Once it reaches 0, I can click that button again and continue hunting ghosts.

I look around for a solution for automating the process. There are quite a few options:

  • Python’s scrapy
  • Phantom.js or Casper.js
  • Puppeteer
  • Tampermonkey (Google Chrome only)

I have some experience in Scrapy, Phantom.js; so I was thinking, why not utilize what I have already known?

… Or I can jump into new things and learn how to do it.

 

I decided to go with the second option, and I chose Tampermonkey to do the work.

 

So I quickly wrote a small script to check if the button is active, and if so, click on it.

setTimeout(() => {
  if (!$('#huntBtn').hasClass('disabled')) {
    $('#huntBtn').trigger('click');
  }
}, 3000);

Simple, isn’t it?!

The problem is, I would like to have the script runs on multiple machines, and I want to synchonize the code in all machines, so if I update the code I’ll just need to do it once.

I ended up with the following structure:

  • A github project to host the code
  • Tampermonkey’s ability to refer to external javascript resources.

My Tampermonkey script

// ==UserScript==
// @name         ghost-trapper
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Ghost Trapper auto
// @author       lightbringer
// @noframes
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @require      file:///Z:/nodejs/ghost-trapper/ghost-trapper.js
// @match        http://www.ghost-trappers.com/fb/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

My ghost-trapper.js

class GhostTrapper {
  constructor() {
    // do stuff
  }
  
  hunt() {
    if (!$('#huntBtn').hasClass('disabled')) { 
      $('#huntBtn').trigger('click'); 
    }
  }
  
  run() {
    setTimeout(this.hunt, 3000);
  }
}

var gt = new GhostTrapper();
gt.run();

 

Looking good, however, the script requires 2 files, one from jquery CDN. one from my local machine. And the speed is not very nice (took me a noticeable time before the hunt click happened).

I thought, why can’t I combine them into 1 file? I have experience in Webpack, so let’s try it out.

 

First I installed necessary packages

npm i --save jquery
npm i --save-dev webpack webpack-cli babel-core babel-loader babel-preset-env

Then I created webpack config file

// webpack.config.js
const path = require('path');
module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'ghost-trapper.bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
};

Then I create a main file for webpack to point to.

// src/main.js 
import $ from 'jquery'; 
import GhostTrapper from './GhostTrapper'; 
const gt = new GhostTrapper(); 
gt.run();

 

After all this, I compile webpack config. And voilà, the bundle is ready for me to include into tampermonkey, and it runs exactly what I wanted.

From then on, I added multiple features to my automation tool. The ease of webpack’s watch mode really helps me during development, as I just need to refresh the page and the new code is already deployed.