Introduction
to
React and Redux

 

Amanpreet Singh

@apsdehal

Agenda

  • Intro to JS Frameworks and their architectures
  • React Philosophy and Virtual DOM
  • Two way and One way data bindings
  • Demo and Code explaining data flow in React  
  • Redux motivation and architecture principles
  • Code changes for Redux

What is JS Framework?

A set of opinionated guidelines and code to model a client side application

Why use a JS Framework?

  • Organization and structure

  • Separation of concerns

  • DRY

  • Performance

  • Security and best practices

Remember
jQuery is not a framework

A library for creating user interfaces

General JS Framework Architecture

Model

Controller

View

Data

Stitching Logic

Template

MVC / MVVM / MV*

Controller, View and Model are often coupled.

 


When you change one, you often have to change other

 

React Philosophy

  • Build UI components which are cohesive units

  • UI logic and UI presentation are tightly coupled and should be together

  • Render all UI through JavaScript

Performance Best Practices

  • Avoid expensive DOM operations

  • Don't access DOM unnecessarily

  • Minimize DOM rerendering (tweaks)

  • All updated elements should be inserted into DOM together

More information on my other slide

React

Rerender everything on every update

But won't that be expensive

Enter Virtual DOM

Rerender everything on every update:

 

  • Generate light description of Component's current state

  • Diff it with old one

  • Compute the minimum changes to be applied to the DOM

  • Apply all the changes in batch updates

  • Efficient update of subtree only

Virtual DOM

Virtual DOM

Data Binding

React has one way data binding

One-way Data Binding

Title text field

let title = "";

Event

Watcher

Two-way Data Binding

Title text field

let title = "";

Watcher

Watcher

Data Binding

React does one job and does it perfectly

JSX

  • JavaScript syntax that looks like XML

  • Helps in writing HTML embedded inreact components 

// Input JS
var component = <Viewer backgroundColor="#fff"/>

// Output JS
var component = React.createElement(Viewer, {backgroundColor: '#fff'});

Demos & Code

A simple search application

Init

Use create-react-app to init most projects

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  render() {
    return <div>Hello</div>
  }
}

Create Components

  • Extend React.Component class to create components
  • Implement render function and return UI description from it
import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  render() {
    return <div>Hello</div>
  }
}

ReactDOM.render(
  <App/>,
  document.getElementById("app")
);

Create Components

  • Root level component will be attached to DOM element

Component Composition

  • Use components in one another

  • Supports reusability and consistency in UI

Component Composition


class App extends React.Component {
  render() {
    return (<div>
              <SearchBar/>
              <Results/>
            </div>);
  }
}

Component Composition

/* Start SearchBar.js */

class SearchBar extends React.Component {
  render() {
    return <input/>
  }
}

export default SearchBar;
/* End SearchBar.js */

/* Results.js */
class Results extends React.Component {
  render() {
    return <div>{heroes[0]}</div>
  }
}

export default Results;

/* End Results.js */

Let's create some component first

Component Composition

import React from 'react';
import ReactDOM from 'react-dom';

import SearchBar from './Searchbar';
import Results from './Results';

class App extends React.Component {
  render() {
    return (<div>
              <SearchBar/>
              <Results/>
            </div>);
  }
}

Import now

Data Flow

From parent to child. Data flows down in react.

 

  • Passed through props, which can be access using {this.props} in the child
  • Props are immutable

Data Flow

/* Child */
class Name extends React.Component {
  render() {
    return <div>{this.props.firstname}  {this.props.lastname}</div>;
  }
}

/* Parent */
class Parent extends React.Component {
  render() {
    return <div>
             <Name firstname="Amanpreet" lastname="Singh"/>
           </div>
  }
|

Reverse Data Flow

From child to parent

 

  • Event handler is passed to child through props by parent
  • Child calls the event handler passed on occurence of the respective event

Reverse Data Flow

/* Child */
class Name extends React.Component {
  handleClick(e) {
      this.props.onClickHandler(e, this.props.firstname + " " + this.props.lastname);
  |
  render() {
    return <div onClick={this.handleClick}>{this.props.firstname}  {this.props.lastname}</div>;
  }
}

/* Parent */
class Parent extends React.Component {
  onClickHandler(e, val) {
      console.log(val)
  }
  render() {
    return <div>
             <Name 
                firstname="Amanpreet" 
                lastname="Singh" 
                onClickHandler={this.onClickHandler}/>
           </div>
  }
|

State

  • Similar to props, but private and controlled by component

  • Initialized in constructor, rerenders the component whenever the state changes

  • Props are fixed for lifetime, use state for data that is going to change

  • Remember the watcher we talked about in one way binding?

State

Adding state to parent

class App extends React.Component {
  state = {
    selectedHeroes: []
  };
  
  handleSearch(e) {
    let searchValue = e.target.value;
    if (searchValue.length === 0) {
      this.setState({selectedHeroes: []})
      return;
    }
    
    let filteredHeroes = heroes.filter((hero) => {
      if (hero.indexOf(searchValue) > -1) {
        return true;
      } else {
        return false;
      }
    });

    this.setState({selectedHeroes: filteredHeroes});
  }
  
  render() {
    return (<div>
              <SearchBar handleSearch={this.handleSearch.bind(this)}/>
              <Results selectedHeroes={this.state.selectedHeroes}/>
            </div>);
    
  }
}

State

SearchBar

class SearchBar extends React.Component {
  handleChange(event) {
    this.props.handleSearch(event);
  }
  render() {
    return <input type="search" onChange={this.handleChange.bind(this)}/>
  }
}

State

Pass state to Results

class Results extends React.Component {

  render() {
    let results = this.props.selectedHeroes.map((hero, index) => {
      return <div key={index}>{hero}</div>;
    });
    return (
      <div>{results}</div>
    );
  }
}

Presentational and Container Components

  • Better separation of concerns

  • Skinny vs Fat controllers
  • Eventually, we end up we too much props passing
  • This helps with that
  • Read more at https://goo.gl/ypJ1TP

Presentational and Container Components

const SearchBar = (props) => {
    return <input type="search" onChange={(e) => {props.handleSearch(e)}}/>
}

const Results = (props) => {
    let results = props.selectedHeroes.map((hero, index) => {
      return <div key={index}>{hero}</div>;
    });
    return (
      <div>{results}</div>
    );
}

Redux

Predictable State Container for JavaScript Apps

Motivation

  • As applications become complex, managing state becomes pain

  • Libraries like react remove asynchronicity and direct DOM manipulation but you still need to maintain state

  • State bloats with every increasing demands of a UI platform

Three Principles

  • Single source of truth
  • State is read only, it can be only changed through actions
  • Changes are made through pure functions called reducers

Single Source of Truth

The state of your whole application is stored in an object tree within a single store.

 

State is read-only

The only way to change the state is to emit an action, an object describing what happened.

 

Changes are made with pure functions

To specify how the state tree is transformed by actions, you write pure reducers.

These reducers take in old state and an action and return back new state 

 

Basic Principle

(previousState, action) => newState

Data Flow Diagram

Example

Changes

const { combineReducers, createStore, dispatch } = Redux;
const { connect, Provider } = ReactRedux;

Add imports

Add reducers

const selectedHeroesReducer = (state = [], action) => {
  switch(action.type) {
    case 'UPDATE_RESULTS': {
      let searchValue = action.event.target.value;
      if (searchValue.length === 0) {
        return []
      }

      let filteredHeroes = heroes.filter((hero) => {
        if (hero.indexOf(searchValue) > -1) {
          return true;
        } else {
          return false;
        }
      });
      return filteredHeroes
    }
    
    default: {
      return state;
    }
  }
}

const reducers = combineReducers({
  selectedHeroes: selectedHeroesReducer
});

Add actions


const actions = {
  updateResults: (e) => {
    return {
      type: 'UPDATE_RESULTS',
      event: e
    }
  }
};

Create Store


let store = createStore(reducers);

Update Presentational Components


const SearchBar = (props) => {
    return <input type="search" onChange={(e) => {props.handleSearch(e)}}/>
};

const mapDispatchToPropsForSearchBar = (dispatch) => {
  return {
    handleSearch: (e) => dispatch(actions.updateResults(e))
  }
};

const VisibleSearchBar = connect(null, mapDispatchToPropsForSearchBar)(SearchBar);

const Results = (props) => {
    let results = props.selectedHeroes.map((hero, index) => {
      return <div key={index}>{hero}</div>;
    });
    return (
      <div>{results}</div>
    );
};

const mapStateToProps = (state) => {
  return {
    selectedHeroes: state.selectedHeroes
  }  
};

const VisibleResults = connect(mapStateToProps)(Results);

Update Container Components


class App extends React.Component {
  render() {
    return (<div>
              <VisibleSearchBar/>
              <VisibleResults/>            
            </div>);
    
  }
}


ReactDOM.render( 
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById("app")
);

Thanks for your attention!

Questions?

Introduction to React and Redux

By Amanpreet Singh

Introduction to React and Redux

  • 889