Notes On React JS

Table of Contents

Overview

Synopsis

my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── serviceWorker.js

// =================   index.js =============================

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

// Supports JSX to render elements.

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();     


// =================   App.js =============================
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

JSX

The JSX syntax is like regular HTML syntax with following exceptions:

  • Can reference Javascript variable using {var_name} syntax. Example :

    const myGreeting = "Hello World!"
    <div>  {myGreeting} {myFunc()} </div>
    
  • The style attributes must be specified like below:

    style={{ 'color' : 'red' }}
    
  • class must be renamed to className since class is keyword :

    <div className='Wrapper'> Hello </div>
    
  • The label "for" attribute must be renamed to htmlFor :

    <label className="label" htmlFor="name"> Enter Name: </label>
    <input id="name" type="text" />
    

React and JQuery

Notes

  • Using JQuery with React not recommended but possible.

  • React uses virtual DOM (VDOM) and syncs with real DOM using ReactDOM.

  • In components it is best not to rely on IDs because the same component can be rendered multiple times!!! The best practice is to attach event handler directly to the component. i.e. Instead of :

    $('#button').onClick(e => ... )  
    
    // Do the following instead ...
    
    function Button(props) {
      return <button onClick={props.onClick}>Say Hello</button>;
    }
    
    function HelloButton() {
      function handleClick() { alert('Hello!'); }
      return <Button onClick={handleClick} />;
    }
    
  • You can use React only partially in a non-React application. Gradually, you can migrate everything to React, if you like:

    ReactDOM.render(
      <HelloButton />,
      document.getElementById('container')
    );
    
  • You can use ref mechanism to get hold of child DOM nodes in parent. This may be useful in cases such as: Input Focus, Trigger animation and integration with third party library. Note that we want to avoid unnecessary re-rendering and also any out-of-sync operations wrt react state.

FAQ

Changing default port 3000

Synopsis:

To create a base React project all 3 followings cmds are equivalent :
  $ yarn create react-app my-awesome-app
  $ npm init react-app my-awesome-app
  $ npx create-react-app my-awesome-app

Run app in development mode:
  $ npm start

To Change default port number 3000 (which is also same for express app),
do one of following:

  # -------------Set env variable--------------------
  $ export PORT=8000
  $ npm start

  # ------------ Change package.json ----------------
  Change package.json ...

  …
    "scripts": {
      "start": “PORT=8000 react-scripts start",
      "build": "react-scripts build",
    }
  …
  # ------------- Change .env file in project root dir -------
  PORT=8000

Entry Point for React App

The core of React App is index.html + index.js; All routes are directed to index.html and it is capable of rendering different results as needed.

The npm build process generates integrated html + js files ready to be hosted on any static webserver (e.g. Apache Server).

Note that the index.js refers to :

ReactDOM.render(<App />, document.getElementById('root'));    

The div with id=root is defined in the index.html.

In addition using a server worker with a cache-first strategy offers performance advantages. Is this enabled by default ?? Runs in client side, but rendered in server side ???

Hosting React App without server

  • It is not at all common use case. In most cases you use it along with a server.

  • npm run build - Creates ./build/ dir with output files. Anyway this is a required step to deploy in production with or without server.

  • Alternatively, you can use webpack or parcel. You may have to include react library in your index.html as below :

    <!-- Load React. -->
    <!-- Note: when deploying, replace "development.js" 
               with "production.min.js". -->
    <script src="https://unpkg.com/react@16/umd/react.development.js" 
                  crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" 
                  crossorigin></script>
    
    <!-- Load our React component. -->
    <script src="like_button.js"></script>
    

Deploy options for React App

  • See https://facebook.github.io/create-react-app/docs/deployment

  • You can deploy with your Apache server.

    • You must use npm run build to create ./build output.
    • Remember to route all incoming URLs to index.html using .htaccess
  • Deploy using a node server:

    npm install -g serve
    serve -s build -l 4000       # It is a webserver listen to port 4000
    
  • Deploy along with node express :

    const express = require('express');
    const path = require('path');
    const app = express();
    
    # /static path maps to ./build/static dir.
    app.use(express.static(path.join(__dirname, 'build')));
    
    # Unknown paths map to React app.
    app.get('/*', function(req, res) {
      res.sendFile(path.join(__dirname, 'build', 'index.html'));
    });
    
    app.listen(9000);
    

Support for Proxy server

How to use another (express) server to forward selectively some URLs from a React Application ...

Let us assume there is express server running at port 3001 ...

  • Change package.json file of React App to add: "proxy": "http://localhost:3001"
  • With in react app, use fetch('/my/path') which fetches express server <http:/localhost:3001/my/path> automatically. Note that main React server may be running at port 3000.
  • See https://www.twilio.com/blog/react-app-with-node-js-server-proxy

Ready to use CSS

  • You can import semantic-ui.min.css and use it for different things like comments etc.

Fake Data Library

npm install faker

Enable CORS

The web server serving the files must allow CORS so that the AJAX requests can connect to AWS services directly.

For Node servers :

res.header("Access-Control-Allow-Origin", "*");

# You can also use a middleware to enable CORS. E.g. Express

app.use(function(req, res, next){

   res.header("Access-Control-Allow-Origin", "*");

   next();

});

For Apache :

# Add following in .htaccess file
Header always set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"

npm run eject

The create-react-app gives you a fast and easy way to get up and running. It encapsulates the project setup and gives you tools to create production ready apps.

When you run npm eject it gives you access to all config files. This can be useful if you want to edit the webpack setup but for a beginner it's not needed.

Local Storage in React

// setter
localStorage.setItem('myData', data);

// getter
localStorage.getItem('myData');

// remove
localStorage.removeItem('myData');

// remove all
localStorage.clear();

The session storage is similar to local storage but deleted when the browser tab is closed. Session storage survives same page reloads.

The same origin policy applies for both local and session storage.

this.props.children

It displays the children components. Note that, by default all children components are hidden until the container component decides to reveal it using this.props.children or such methods.

const Picture = (props) => {
      return (
        <div>
          <img src={props.src}/>
          {props.children}
        </div>
      )
}

//App.js
render () {
  return (
    <div className='container'>
      <Picture key={picture.id} src={picture.src}>
          //what is placed here is passed as props.children
      </Picture>
    </div>
  )
}

Passing Data to children

render() {
  const children = React.Children.map(this.props.children, child => {
    return React.cloneElement(child, {
      someData: this.props.someData
      someState: this.state.someState
      someFunction: x => x
    });
  });
return (
    <div>
      { children }
    </div>
  )
}

withRouter

withRouter will pass updated match, location, and history props to the wrapped component whenever it renders:

* You can get access to the history object's properties and 
* the closest <Route>'s match 

import React from "react";
import { withRouter } from "react-router";

// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {

  render() {
    const { match, location, history } = this.props;

    return <div>You are now at {location.pathname}</div>;
  }
}

// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation);

Material UI Notes

Using Icons

yarn add @material-ui/icons
# import MenuIcon from '@material-ui/icons/Menu';
# use the icon : <MenuIcon/>

Caveats

  • You can use Function based (simple) or Class based (peferred) components.

  • State can be updated only by using setState() function.

  • Arrow functions does auto binding to this object properly. Otherwise, you may have to do this sometimes :

    class my_class { 
          my_func() { ... } 
          this.my_func.bind(this);
          my_func = () => { ... }; # Binding is better and automatic.
    }
    
  • For AJAX requests, use built-in Fetch() or 3rd Party axios package (better) :

    axios.get(url,  { headers : ... }).then( response =>  {... } ))
    // Using async / await is even better.
    response = await axios.get(...)
    

Stephen Grider React Redux Notes

Basics, Components, etc.

  • See https://github.com/StephenGrider/redux-code for good reference.

  • React's props system:

    • There is component hierarchy

    • props is a system to pass data from parent to child for configuring child.

    • Parent can pass different props for each child (obviously) :

      <AllAuthors> <Author name='Raja' /> <Author name='Rani' /> </AllAuthors>
      import faker from 'faker'
      <Author name='Raja' avatar={faker.image.avatar()}  />
      // avatar is a link to an image.
      const Author = (props) => { 
         return   <div> <img src={props.avatar}> </div> 
      }
      
    • Nested components passed as props.children for parent object to render :

      const AllAuthors = (props) => {
         return  <div className='info'>   {props.children} </div>
      }
      
  • Custom UI components can be easily built like above. e.g. AuthorCard above.

  • Semantic UI can be imported like below:

    - Directly in index.html: blog/public/index.html ... href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"

  • You must use class based components if you have to handle user actions (like clicks). Simple rendering components are perfect for functional components.

  • Advantages of class based Components:

    • Can handle user actions (like clicks)

    • Can maintain states (a react system)

    • Can understand lifecycle events (Do something when system starts)

    • Perfect to display forms with event handlers (onChange on input box, submit, etc).

    • Example event handler on input box in form :

      <input type="text" onChange={this.onInputChange}>
      class  SearcBar extends Component { 
        onInputChange(event){
           console.log(event.target.value)
        }
        ...
      }
      
      For simple callbacks :
      <input type="text" onChange={ (event) => console.log(event.target.value)}>
      
      You can maintain state like this:
      <input type="text" onChange={ (e) => this.setState(term : e.target.value)}>
      
      // The "input" box is called "controlled" element if we maintain state.
      
      // This is typical way to prevent default form submit ...
      
         <form onSubmit={onFormSubmit} ... > 
            onFormSubmit(event){
                event.preventDefault(); ...
                // this.state.term does not fetch instance variable here !!!
            }
      
      // Only for event handlers, it is typical to bind from constructor like below:
      class  SearcBar extends Component { 
        constructor(props){
           super(props)
           this.onInputChange = this.onInputChange.bind(this)
        }
        ...
      }
      
      // Preferred style for event handlers is to use arrow function instead of bind
      onInputChange = (event) => { console.log(this.state.term);  ... } 
      // Inline arrow functions also has proper this access ...
      <form  onSubmit={ (e) => this.setState( { term: e.target.value } ) } ... >
      
  • The React.Component supports single element called state which can be set by setState() API only :

    class App extends React.Component {
       constructor(props){
          super(props)         // Mandatory.
          this.state = {}      // Here after modifiable through setState() only.
    
          this.state = {language: 'English'}  // E.g. Set default language.
          this.state = {location: null}  // Use null for 'I dont know'.
                                         // null is different from undefined.
    
          window.navigator.geolocation.getCurrentPosition(
    
                // Success callback must call this.setState() not this.state.
                position => this.setState({ lat: position.coords.latitude }),
    
                err => this.setState({ errorMessage: err.message })
          );     
    
       }
    }
    
  • The default constructor calls super(props). So it is optional.

  • The class level variables initialization behaves as if they are called from constructor :

    class App extends Component {
       state = { lat : null }    // This is called from constructor.
                                 // babel transpiles to move into constructor.
    }
    
  • Component life cycle methods time series:

    • constructor
    • componentWillMount // this.setState() does not trigger render()
    • render // Content visible on screen
    • componentDidMount // Good place to do dataloading here and later at DidUpdate.
    • componentDidUpdate // this.setState() triggers render() and this as well.
    • componentWillUnmount
  • For example do following. Perfect alternative to doing from constructor. :

    componentDidMount() {
          window.navigator.geolocation.getCurrentPosition(...)
    }
    
  • These life cycle methods support "Progressive Loading" of components trying to render all components first then loading the data in background.

  • Note: Javascript supports static methods but not static variables inside class. This can be achieved by following :

    class Animal {
      count = 0              // This is instance variable (not static).
      constructor() { }
    
      static increaseCount() { this.count += 1; }
    
      static getCount() { return this.count; }
    }
    
    Animal.count = 0;        //  <====  This defines static property!!!
    
    var cat = Animal()
    var dog = Animal()
    Animal.increaseCount(); Animal.increaseCount();
    console.log(Animal.getCount());     // Output:   2
    console.log(cat.count)              // Output:  0
    console.log(dog.count)              // Output:  0
    
  • Props system only supports passing props from parent to child.

  • To communicate from child to parent, pass callback as prop to child, then child will call the callback. (This is similar to form element manipulating our App Component state).

  • When <App> ... <SearchBar> .. </App>, you can pass callback like below.

class App extends Component {
  render() {
    searchCars(term) { .... }

    return (
        <div>
           <SearchBar searchCarsFromApp={this.searchCars}> ... </SearchBar>
        </div> )
  }
}

// From with in SearchBar you will refer to this like ...
class SearchBar extends Component {
   searchBarOnSubmit(event){
       event.preventDefault();
       this.props.searchCarsFromApp(this.state.term);
   }
}
<form  onSubmit={searBarOnSubmit} ... >
  • For React application AJAX client, we use axios library rather than Fetch API.
  • By prefixing a function or arrow function with "async" we make it asynchronous:
    • With in async function you can call "await another_api_returing_promise()";
    • Typically await statements trigger some sort of yielding OS calls such as socket select or send or file read or write. These are ideal async actions, so we typically provide "promise" APIs for all I/O and network calls.
    • You must call "promise returning" functions by passing callbacks. This looks ugly. If you want to "simulate synchronous waiting" just prefix that call with "await". The await does not really mean "synchronous waiting", but executes the subsequent statements after the completion of API call asynchronously.
  • Organize local modules making api calls inside api/ directory.
  • We can Memoize to cache JavaScript function results and speed up your code. Use lodash _.memoize(fn) to memoize the function.

React Router

  • Remember, react-router is just a set of cleverly implemented vanilla react components that can be used to simulate a server-ish side route handler.

  • BrowserRouter is a React Component that watches the URL and more or less passes the current path down to its children, while a Route component will render some other component based on the URL information passed to it by its parent BrowserRouter.

  • HashRouter is better to serve static files. All routes have # suffix. e.g. url#suffix

  • MemoryRouter does not use URL for navigation.

  • IndexRoute is used to set a default view when rendering a specific Route Component.

  • You use BrowserRouter and Route :

    import {BrowserRouter, Router, Route, Switch} from 'react-router-dom'
    
  • Examples :

    <Route path="/" component={App}>
        <IndexRoute component={Home}/>             # Deprecated
        <Route path="about" component={About}/>      
        <Route path="users" component={UsersCommon}/>    # Matches users/ and users/*
        <Route exact path="users" component={Users}/>    # Matches users/ only
    </Route>
    
    function UsersCommon() {
      return (
        <Switch>
          <Route exact path='/users' component={UserHeaderTitle}/>
          <Route path='/users/:number' component={SingleUser}/>
        </Switch>
      );
    }
    
    // /users/6 Gets { number : 6 } in params. i.e. props.match.params.number == 6
    function SinglePlayer(props) {
       console.log(props.match.params);  // Displays { number : 6 }
       ...
       return (<div> User Id is : {props.match.params.number} </div> )
    }
    
  • Never use <a> anchor tags. Use Link. This gets translated to manage SPA :

    <ul> <li><Link to='/'>Home</Link></li> </ul>
    
  • The <Link> element should appear inside <BrowserRouter> or <Router>

  • The App can look like this:

    const App = () => {
     return (
       <div className="ui container">
         <Router history={history}>
           <div>
             <Header />
             <Switch>
               <Route path="/" exact component={StreamList} />
               <Route path="/streams/new" exact component={StreamCreate} />
               <Route path="/streams/edit/:id" exact component={StreamEdit} />
               <Route path="/streams/delete/:id" exact component={StreamDelete} />
               <Route path="/streams/:id" exact component={StreamShow} />
             </Switch>
           </div>
         </Router>
       </div>
     );
    };
    

Authentication

  • OAuth authentication

  • Supports list of scopes (permissions).

  • Google API scopes: email, profile, openid

  • gmail scopes look like url e.g. http://gmail.com stands for all permissions for mail.

  • OAuth for Servers

  • OAuth for Browsers

  • For single page application to be able to interact with Google API, do following:

    • Create a project in console.developers.google.com

    • Generate OAuth clientID (not API Key)

    • Call Google API library at client, on clicking 'Login with Google'

    • Enable domains from which the call is allowed e.g. example.com Note that localhost:3000 is allowed as a domain!

    • clientID and clientSecret both are generated. We are not going to use client-secret !!!

    • Google client api is included in header of index.html: <script src="https://apis.google.com/js/api.js" /> There is a global object window.gapi available now.

    • gapi.load('client:oauth2') // Load additional library
      // Ideal to do from ComponentDidMount()

    • gapi.client.init(clientId : xxxx , scope: 'email') // Requesting email scope.

    • this.auth = gapi.auth2.getAuthInstance(); // Instantaneous.

    • this.onAuthChange(this.auth.isSignedIn.get())

    • this.auth.isSignedIn.listen(this.onAuthChange)

  • Trigger API call :

    gapi.auth2.getAuthInstance().signIn()
    

Build Rest API

  • Use json-server nodejs package: Get a full fake REST API with zero coding in less than 30 seconds (seriously) See https://github.com/typicode/json-server :

    npm install json-server
    json-server --watch db.json
    
    db.json
    {
      posts : [ { ... }, {...} ],
      ....
    }
    
  • JSONPlaceholder Fake Online REST API for Testing and Prototyping :

    fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(response => response.json())
    .then(json => console.log(json))
    
    // Supported APIs:
    
        /posts  100 posts
        /comments   500 comments
        /albums 100 albums
        /photos 5000 photos
        /todos  200 todos
        /users  10 users
    

Function Hooks

  • Latest feature.
  • Includes redux like functionality to re-render the component automatically on value change.
  • Supported basic hooks are: useState, useContext, useEffect, etc
  • Components are more powerful, but they have to render some UI. Functions return values (could be html, but not necessarily) and are easy to reuse vs Classes. Currently function can not have local state, but classes do. But reusing classes is much harder.
  • Building blocks of React:: state, lifecycle, and context.
  • Hooks are fully encapsulated — each time you call a Hook, it gets isolated local state within the currently executing component.
  • See https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889

Synopsis:

// Basic
import React, { useState } from 'react';

const App = () => {

  // useState(someValue) returns plain array of size 2.
  // Elements are unnamed and you can name them anything you like. 

  const [resource, setResource] = useState('posts');
  const [name, setName] = useState('Raja');
  console.log('resource: ', resource) // Simple string. 
                                      // Could be number or object, ...
  console.log('setResource: ', setResource)  // It is a function.

  return (
    <div>
      <div>
        <button onClick={() => setResource('posts')}>Posts</button>
        <button onClick={() => setResource('todos')}>Todos</button>
        <button onClick={() => setName('Rani')}>Rani</button>
        <button onClick={() => setName('Raja')}>Raja</button>
      </div>
      Current resource: {resource} <br/>
      Current name: {name} <br/>
      As you click, the whole component re-renders.
    </div>
  );
  • What/Why useEffect() ? See Also: https://stackoverflow.com/questions/53051465/react-hooks-what-why-useeffect:

    For functional components, we miss ability to specify componentDidMount() 
    and componentDidUnmount() callbacks. The useEffect() just provides that. 
    It could be specified multiple times for different concerns.
    
     componentDidMount() {
       prepareBehaviourOne();
       prepareBehaviourTwo();
     }
    
     componentDidUnmount() {
       releaseBehaviourOne();
       releaseBehaviourTwo();
     }
    
     becomes:
    
     useEffect(() => {
       prepareBehaviourOne();
       return releaseBehaviourOne;
     });
    
     useEffect(() => {
       prepareBehaviourTwo();
       return releaseBehaviourTwo;
     });
    
  • Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint, during a deferred event. Advantage is that it does not block render(), the disadvantage is that you can not block the initial render() and it results in multiple renders ??

  • Calling of useEffect() can optimize the re-render only during initial draw or specific state change:

    const [count, setCount] = useState(0);
    // Following callback only when count changes.
    useEffect( () => document.title = 'count changed',  [count])
    useEffect( () => document.title = 'Called only during initial',  [])
    useEffect( () => console.log('Called after initial and after every state update')
    
  • Recursion Alert! You can not call setMyCounter() method from useEffect() function if that function is triggered for myCounter state change. But you can call setAnotherCount() as long as there is no recursive effect.:

    useEffect( () => /* You should not call setCount() here.
                        But you can call setAnotherCount() here. */,  [count])
    
  • When does React re-render?

  • Classic example of designing a component which has to re-render on width change, by sharing the custom hooks code. This code is from Dan Abranov :

    // MyResponsiveComponent.js
    
    function MyResponsiveComponent() {
      const width = useWindowWidth(); // Our custom Hook
      return (
        <p>Window width is {width}</p>
      );
    }
    
    // useWindowWidth.js   Defines custom hook.
    
    function useWindowWidth() {
      const [width, setWidth] = useState(window.innerWidth);
    
      useEffect(() => {
        const handleResize = () => setWidth(window.innerWidth);
        window.addEventListener('resize', handleResize);
        return () => {
          window.removeEventListener('resize', handleResize);
        };
      });
    
      return width;
    }
    

Redux Thunk

  • Exists to handle async calls like external REST APIs.

Case Study: aws-samples/lambda-refarch-imagerecognition