React

Lesson 1

Agenda

  • History
  • About React
  • Introduction to JSX
  • Sample Component, dom.render
  • State
  • Components and Props
  • Rendering and render cycle
  • Installation

History

About React

Add React to web site

  • Manually modify .html
  • Manually build using ES6 modules  (using tools like webpack or similar)
  • Automated using Create-React-App    (or other start kits)

Manually add React

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Add React in One Minute</title>
  </head>
  <body>

    <h2>Add React Blog in One Minute</h2>
    <p>This page demonstrates using React with no build tooling.</p>
    <p>React is loaded as a script tag.</p>

    <!-- We will put our React component inside this div. -->
    <div id="react_app_root"></div>

    <!-- 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="blog.js"></script>

  </body>
</html>

index.html

Manually add React

'use strict';

const title = 'Hey, I have just joined React pro-camp';

const element = React.createElement(
      'div',
      {style: {color: 'darkorange'}},
      this.state.post.title
);

const reactRootDomNode = document.querySelector('#react_app_root');
ReactDOM.render(element, reactRootDomNode);

blog.js

Manually add React ES6

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

const title = 'Hey, I have just joined React pro-camp';

const element = React.createElement(
      'div',
      {style: {color: 'darkorange'}},
      this.state.post.title
);

const reactRootDomNode = document.querySelector('#react_app_root');
ReactDOM.render(element, reactRootDomNode);

blog.js

<!DOCTYPE html>
<html>
...
  <body>
    ...
    <div id="react_app_root"></div>
    ...
    <!-- Load React and our component. -->
    <script src="blog.js" type="module"></script>

  </body>
</html>

index.html

React ES6 + Builder

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

const title = 'Hey, I have just joined React pro-camp';

const element = React.createElement(
      'div',
      {style: {color: 'darkorange'}},
      this.state.post.title
);

const reactRootDomNode = document.querySelector('#react_app_root');
ReactDOM.render(element, reactRootDomNode);

blog.js

<script src="bundle.js"></script>

index.html

blog.js

Webpack

Rollup

Parcel, etc...

bundle.js (react, react-dom, blog.js)

Create React App

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

const title = 'Hey, I have just joined React pro-camp';

const element = React.createElement(
      'div',
      {style: {color: 'darkorange'}},
      this.state.post.title
);

const reactRootDomNode = document.querySelector('#react_app_root');
ReactDOM.render(element, reactRootDomNode);

index.js

>>> npx create-react-app my-blog
>>> cd my_blog
>>> npm run build

JSX

React createElement

const element = React.createElement(
               'div', {style: {color: 'darkorange'}}, 'My First Post'
);
import React from 'react';
import ReactDOM from 'react-dom';

const element = <div style={{color: 'darkorange'}}>'My First Post'</div>

const reactRootDomNode = document.querySelector('#react_app_root');
ReactDOM.render(element, reactRootDomNode);

HTML tag

(or React Component)

HTML attributes

(or React properties)

Content (children)

JSX

const element = <div>My First Post</div>;

const element = <div id="post-title">My First Post</div>;

const element = (<div id="post-title">
    My First Post
</div>);

const element = (<div id="post-title">
    <h1>My First Post</h1>
    <p>Hey, I have just started React pro-camp</p>
</div>);

JSX features

const user = { firstName: 'Viktor', lastName: 'Shevchenko'};

function formatName(user) {
    return user.firstName + user.lastName;
}

const element = <h1>Hello, {formatName(user)}</h1>;

Use {variableName} to access data from scope

JSX magic

In order to code with JSX syntax become a valid JavaScript code it has to be tranformed.

JSX

Transformer

TypeScript

Babel

JSX Transformation

const element = <div>My First Post</div>;
const element = React.CreateElement('div', null, 'My First Post');

JSX Transformation

const element = <div id="post-title">My First Post</div>;;
const element = React.CreateElement('div', {id: 'post-title'}, 'My First Post');

JSX Transformation

const element = (<div id="post-title">
    <h1>My First Post</h1>
    <p>Hey, I have just started React pro-camp</p>
</div>);
const element = React.CreateElement('div', {id: 'post-title'}, 
    React.CreateElement('h1', null, 'My First Post')
);

JSX Transformation

const element = (<div id="post-title">
    <h1>My First Post</h1>
</div>);
const element = React.CreateElement('div', {id: 'post-title'}, 
    React.CreateElement('h1', null, 'My First Post')
);

React Component

function Greeting(props) {
  return <h1>Hello, {props.name}</h1>;
}
class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Function

ES6 Class

const element = <Greetitg name="Viktor" />;

// the same as

const element = React.createElement(Greeting, {name: 'Viktor'}, null)
ReactDOM.render(element, document.getElementById('root'));
const element = {
    type: Greeting,
    props: {
        children: {
            type: 'h1',
            props: {
                children: 'Hello Viktor'
            }
        }
    }
}

React Component

  • Compose Components
  • Extract Components
  • Remember "props" - are read-only
  • ! Do not create components inside other components

Component State

class Randomizer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: Math.rand()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>Value is - {this.state.count}.</h2>
      </div>
    );
  }
}
ReactDOM.render(<Randomizer />, document.getElementById('root'));

// some time later

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

Component State

  • Do Not Modify State Directly

  • State Updates May Be Asynchronous

  • State Updates are Merged

Component Events

class Randomizer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: Math.rand()};
  }

  reset(){
    this.setState({count: 0});    
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>Value is - {this.state.count}.</h2>
        <button onClick={this.reset}>Reset</button>
      </div>
    );
  }
}

this?

class Randomizer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: Math.rand()};
    this.reset = this.reset.bind(this);
  }

  reset(e){
    this.setState({count: 0});    
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>Value is - {this.state.count}.</h2>
        <button onClick={(e) => this.reset(e)}>Reset</button>
      </div>
    );
  }
}

OR

Component Lifecycle

Mounting/Unmounting

class Randomizer extends React.Component {
  constructor(props) {
    super(props);
    this.reset = this.reset.bind(this);
  }

  static getDerivedStateFromProps(props, state){
    // warning. Bad example.
    return !this.state.count && {count: props.count}
  }

  componentDidMount(){
    this.interval = this.setInterval(
      () => this.setState({count: Math.rand()}), 
      1000
    );
  }

  componentWillUnmount(){
    clearInterval(this.interval);
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>Value is - {this.state.count}.</h2>
        <button onClick={this.reset}>Reset</button>
      </div>
    );
  }
}
  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()
  • componentWillUnmount()

Component Lifecycle

Updating

class Randomizer extends React.Component {
  constructor(props) {
    super(props);
    this.reset = this.reset.bind(this);
  }

  shouldComponentUpdate(nextProps, nextState){
    if (nextProps.count === this.props.count) {
      return false;
    }
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>Value is - {this.state.count}.</h2>
        <button onClick={this.reset}>Reset</button>
      </div>
    );
  }
}
  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

Component Lifecycle

Error Handling

class Randomizer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hasError: false}
  }

  static getDerivedStateFromError(error) {
    // Update component state
    return {hasError: true}
  }

  componentDidCatch(error, info) {
    // side effect, log error. 
  }

  render() {
    if(this.state.hasError) {
      return <h2>Error<h2>
    }

    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>Value is - {this.state.count}.</h2>
        <button onClick={this.reset}>Reset</button>
      </div>
    );
  }
}
  • static getDerivedStateFromError()
  • componentDidCatch()

React Magic

  • React Only Updates What’s Necessary

  • Reconciliation 

Installataion

Create React App

Homework

  • Select theme

  • Deploy app

Lesson 2

Agenda

  • Lists and Keys
  • Conditional Rendering
  • Forms on Submit
  • Thinking in React

ReCap

  • What is React Element?
  • What is React Component?
  • difference between JSX and HTML?
  • How React Event is different

Lists and Keys

const MY_FILMS = [
    {
        name: 'Mad Max: Road of Fury',
        description: 'In a post-apocalyptic wasteland, a woman rebels against a 
            tyrannical ruler in search for her homeland with the aid of a group 
            of female prisoners, a psychotic worshiper, and a drifter named Max.',
        rating: 8.1
        tags: ['action', 'apocalyptic', 'post apocalypse', 'desert', 'australia', 'female warrior', 'chase']
    },
    {
        name: 'Logan',
        description: 'In a future where mutants are nearly extinct, an elderly and
            weary Logan leads a quiet life. But when Laura, a mutant child pursued
            by scientists, comes to him for help, he must get her to safety.',
        rating: 7.9
        tags: ['marvel comics', 'x men', 'superhero', 'mutant', 'reluctant hero']
    }

]

Lets build a list of elements based on array

Lists and Keys

render() {
    <div>
        {MY_FILMS.map(film => <div>{film.name}</div>)}
    </div>
}

Use ".map" Array method to render a list

Lists and Keys

What is key?

 
  • Special React property that gives elements stable identity
  • Used by React to track add, remove, change of elements in Array
  • Boost React performance by improving reconciliation
  • Eliminates enexpected data mixes up

Lists and Keys

keys in action

 

Array index

Unique id

<li key={0}>Mad Max</li>
<li key={1}>Logan</li>
<li key={1002}>Mad Max</li>
<li key={1003}>Logan</li>
this.setState([{name: 'Avangers'}, ...this.state.films]);
<li key={0}>Avangers</li>
<li key={1}>Mad Max</li>
<li key={2}>Logan</li>
<li key={1001}>Avangers</li>
<li key={1002}>Mad Max</li>
<li key={1003}>Logan</li>

keys are new!

keys are same!

li[0].textContent = 'Avangers'; // replace 'Mad Max'
li[0].textContent = 'Mad Max'; // replace 'Logan'

var li3 = document.createElement('li');
li3.textContent = 'Logan';

li.parentNode.appendChild(li3)
var li0 = document.createElement('li');
li0.textContent = 'Avangers';

li.parentNode.prependChild(li0)

Lists and Keys

Rules of Keys

 
  • Keys should be assigned to parent components (those in map function)
  • Prefer using unique id over an array indexes
  • keys is an internal React property, that is not passed to the inner component

 

Conditional Rendering

Conditional rendering

Using Regular "if else" Construction

function Rating ({hasVouted}) {

  if (hasVouted) {
    return <div>You have already rated this movie</div>
  }

  return (
    <div>Please rate this movie</div>    
  )
}
class Rating extends React.Component {
    
  render() {
    if (hasVouted) {
      return <div>You have already rated this movie</div>
    }

    return (
      <div>Please rate this movie</div>    
    )
  }
}
<Rating hasVouted={true} />

Conditional rendering

Define new Component variable

function Rating ({hasVouted}) {

  const RatingLabel = hasVouted ? <div>You have already rated this movie</div> : <div>Please rate this movie</div>  

  return (
    <RatingLabel />    
  )
}

Conditional rendering

Using &&

function Rating ({hasVouted}) {

  return (
    {!hasVouted && <div>Please rate this movie</div>}
  )
}

Conditional rendering

Using Ternary Operator

function Rating ({hasVouted}) {

  return (
    {hasVouted ? <div>You have already rated this movie</div> : <div>Please rate this movie</div>}     
  )
}

Conditional rendering

Avoid component render

<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

Handling Events

Handling Events

Practical task

Implement selection on film when clicking on its name.

Display Selected Field Section only if any film is selected

Display description of selected film

Reimplement Selected movie to Edit Movie

Forms

Controlled Components

  • Control State - is kept and fully managed by React
  • Requires to have both value and onChange properties

How to avoid changes in the original component?

Forms

Uncontrolled Components

  • The state is managed by the browser
  • Relies on defaultValue prop

Task: Rebuild with uncontrolled components

"ref" prop

"ref" prop

  • Special React only prop (like Key)
  • React.createRef() - to create a reference
  • May contain HTML Element, Component Instance
  • Not applicable to functional components

"ref" prop

  • Prefer "ref" over document.getElement...
  • Don't overuse refs
  • For example, instead of exposing open() and close() methods on a Dialogcomponent, pass an isOpen prop to it.
  • refs on components

Dom Manipulation

  • implement focus first field

Thinking in React

  • separate components
  • lift state up
  • There should be a single “source of truth”
  • If state involves writing more “boilerplate” code than two-way binding approaches
  • If something can be derived from either props or state, it probably shouldn’t be in the state.

Typechcking

  • prop-types
  • static type checking

Homework

  • style list
  • style form
  • write prop-types
  • make form edit independent from list item
  • implement cancel edit
  • implement image for movie

Lesson 3

Recap

  • why we need keys
  • why we need refs
  • how many ways to do conditional rendering
  • discuss homework

State Management

  • Dumb vs SmartComponents
  •  

Cross Component

Context

Child:

Child.contextTypes = {
  color: PropTypes.string
};

Parent:

getChildContext() {
    return {color: "purple"};
  }

 

Parent.childContextTypes = {
  color: PropTypes.string
};

Lesson 4

Code Reuse

techniques in React

Containment

class Tag extends React.Component {
  render() {
    return <pre>{this.props.tag}</pre>;
  }
}

{tags.map(tag => (
  <Tag key={tag} tag={tag} />
))}
function EmojiBlock(props) {
  return <span>{`🔥`}{props.children}{`🔥`}</span>
}

{tags.map(tag => (
  <EmojiBlock key={tag}><Tag tag={tag}/></EmojiBlock>
))}

Specialization

class Tag extends React.Component {
  render() {
    const {emoji, tag} = this.props;
    return <pre>{emoji}{tag}{emoji}</pre>;
  }
}

{tags.map(tag => (
  <Tag key={tag} tag={tag}/>
))}
function EmojiTag(props) {
    return <Tag {...props} />; 
}

{tags.map(tag => (
  <EmojiTag key={tag} tag={tag} emoji={`❤️`} />
))}

Reuse Statefull Logic

Inheritance

import React from "react";

export default class Emoji extends React.Component {
  constructor(props) {
    super(props);

    this.emojiList = ["🤟", "🔥", "⭐️", "🦄"];

    this.state = {
      emojiIdx: 0
    };
  }

  rotateEmoji = () => {
    const nextIndex = this.state.emojiIdx + 1;

    this.setState({
      emojiIdx: nextIndex === this.emojiList.length ? 0 : nextIndex
    });
  };

  render() {
    null;
  }
}

Inheritance

class EmojiTagInherited extends Emoji {
  render() {
    const emoji = this.emojiList[this.state.emojiIdx];

    return (
      <pre onClick={this.rotateEmoji}>
        {emoji}
        {this.props.tag}
        {emoji}
      </pre>
    );
  }
}
{tags.map(tag => (
  <EmojiTagInherited key={tag} tag={tag} />
))}

Inheritance

Pros

Cons

  • It works
  • 1 React Element created
  • Inheritance complexity
  • Not a React way to do
  • Not applicable to function components

High Order Component

export default function withEmoji(WrappedComponent) {
  return class WithEmoji extends React.Component {
    constructor(props) {
      super(props);

      this.emojiList = ["🤟", "🔥", "⭐️", "🦄"];

      this.state = {
        emojiIdx: 0
      };
    }

    rotateEmoji = () => {
      const nextIndex = this.state.emojiIdx + 1;

      this.setState({
        emojiIdx: nextIndex === this.emojiList.length ? 0 : nextIndex
      });
    };

    render() {
      return (
        <WrappedComponent
          emoji={this.emojiList[this.state.emojiIdx]}
          rotateEmoji={this.rotateEmoji}
          {...this.props}
        />
      );
    }
  };
}

High Order Component

const TagWithEmoji = withEmoji(EmojiTag);


{tags.map(tag => (
  <TagWithEmoji key={tag} tag={tag} />
))}

{tags.map(tag => (
  <EmojiTag key={tag} tag={tag} />
))}
function EmojiTag({ tag, emoji = null, rotateEmoji = () => {} }) {
  return (
    <pre onClick={rotateEmoji}>
      {emoji}
      {tag}
      {emoji}
    </pre>
  );
}

HOC Questions

Mutation of the original component

class Film extends React.Component {
  render() {
    return (
      <div>
          Name: {this.props.name},
          Description: {this.props.description}      
      </div>
    );
  }
}

function withLogProps(Component) {
  InputComponent.prototype.componentDidUpdate = function(prevProps) {
    console.log('Previous props: ', prevProps);
    console.log('Current props: ', this.props);
  };
  
  return Component;
}

const FilmLogger = withLogProps(Film);
  • No more possible to use Film Component separately
  • Impossible to apply 2 mutational HOCs
  • Don't work with functional components
function logProps(Component) {
  return class extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('Previous props: ', prevProps);
      console.log('Current props: ', this.props);
    }

    render() {

      return <WrappedComponent />;

    }
  }
}
      return <WrappedComponent {...this.props} />;

Always bypass unrelated props

HOC Questions

Maximizing Composability

const FilmLogger = withDebugLogProps(Film);
const FilmLogger = withLogProps(Film, 'debug');
const FilmLogger = withLogProps('debug')(Film);
  • function with one argument are easier to compose
  • can act as decorators

HOC Questions

HOC in render method

class FilmLogger extends React.Component {

  render() {
      const FilmWithLogging = withLogProps(Film);

      return (
        <FilmWithLogging />
      )
  }
}
  • do not use
  • cause performance degradation
  • remounting a component causes the state of that component and all of its children to be lost, React reconciliation process recreated a component tree each time
  • ​dynamically apply HOC in lifecycle methods

HOC Questions

HOC copy static methods

class Film extends React.Component {
  render() {
    return (
      <div>
          Name: {this.props.name},
          Description: {this.props.description}      
      </div>
    );
  }
}

Film.defaultProps = {
  name: 'Terminator',
  descriptions: 'Cyborg from future ..... '
}

const FilmLogger = withLogProps(Film);

FilmLogger.defaultProps ??? 
  • do copy
  • use hoistNonReactStatic

HOC Questions

HOC does not pass ref

class Film extends React.Component {
  render() {
    return (
      <div ref={this.props.ref}>
          Name: {this.props.name},
          Description: {this.props.description}      
      </div>
    );
  }
}

const FilmLogger = withLogProps(Film);

...


<FilmLogger ref={c => this.container = c}>
  • ref is a special property, like key
  • use React.forwardRef API (16.3+)

High Order Component

 

Pros

Cons

  • React way, use composition
  • cleaner code
  • Can be used as decorator
  • Has own rules
  • 2 React Elements

Render Props

 
class Emoji extends React.Component {
  constructor(props) {
    super(props);

    this.emojiList = ["🤟", "🔥", "⭐️", "🦄"];

    this.state = {
      emojiIdx: 0
    };
  }

  rotateEmoji = () => {
    const nextIndex = this.state.emojiIdx + 1;

    this.setState({
      emojiIdx: nextIndex === this.emojiList.length ? 0 : nextIndex
    });
  };

  render() {
    return this.props.render({
      emoji: this.emojiList[this.state.emojiIdx],
      rotateEmoji: this.rotateEmoji
    });
  }
}

Render Props

 
function EmojiTag({ tag, emoji = null, rotateEmoji = () => {} }) {
  return (
    <pre onClick={rotateEmoji}>
      {emoji}
      {tag}
      {emoji}
    </pre>
  );
}
{tags.map(tag => (
  <EmojiRP key={tag} render={props => <EmojiTag tag={tag} {...props} />} />
))}

{tags.map(tag => (
  <EmojiRP key={tag} render={props => <EmojiTag tag={tag} />} />
))}


/* props = {emoji, rotateEmoji} */

Render Props

 

Pros

Cons

  • React way, use composition
  • cleaner code
  • Less rules, more obvious props passing
  • 2 React Elements
  • Worse readability when applying several RP

That is it!

Lesson 5

Routing

  • HTML 5 history API
  • Routing concepts
  • Build Router
  • Solutions for routing

HTML History API

История

  • всегда была урла
  • но навигация - запрос к серверу
  • что если страници почти идентичны?

Решения

  • не менять url
  • но тогда нельзя попасть сразу на нужную страницу
  • # based url

Нужна ли url? EE задачи

  • It identifies a unique resource.
  • bookmarking
  • search engine can index it
  • copy and paste
  • url matter

Problem

  • нельзя поменять url без перезагрузки

HTML 5 History API

Основная идея - не менять принцип работы браузера - а дать API для управления url

HTML 4 History API

Navigation

window.history.back();

window.history.go(-1);
window.history.forward();

window.history.go(1);
var numberOfEntries = window.history.length;

HTML 5 History API

Manipulation

window.history.pushState({data: 'test'}, 'page 2', page2.html);

window.history.replaceState({data: 'test'}, 'page 2', page2.html);

window 'popstate' event

(window.onpopstate = funcRef;)

State - The state object is a JavaScript object which is     associated with the new history entry

Title - title for the state to which you're moving

URL - The new history entry's URL. Understand when the   browser does any server request

HTML 5 History API

  • pushState can accept any URL in the current domain vs only hash 
  • You don't have to change the URL if you don't want to. When window.location preserves history only if the hash is the same
  • You can associate arbitrary data with your new history entry. With the hash-based approach, you need to encode all of the relevant data into a short string.
  • If the title is subsequently used by browsers, this data can be utilized (independent of, say, the hash).
  • pushState() never causes a "hashchange" event to be fired, even if the new URL differs from the old URL only in its hash.

Routing

 

  • Static vs Dynamic routing
  • Philosophy of dynamic routing
  • Routing Solutions
  • How To Treat Routing(source of state or result of state)

Routing

Static

Dynamic

  • centralized
  • defined before application render 
  • predefined routes
  • big chunks code splitting
  • easy inspection
  • decentralized
  • defined during application is rendering
  • responsive routes
  • small chunks code splitting
  • more complex inspection

Routing

  • Aviator - Aviator is a front-end router built for modular single page applications. (Example).
  • Backbone - Backbone supplies structure to JavaScript-heavy applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing application over a RESTful JSON interface.
  • component-router: Flux-based routing solution for components
  • Director - A tiny and isomorphic URL router for JavaScript.
  • Finch - A simple, yet powerful, javascript route handling library.
  • mvc-router Use the Model-View-Controller (MVC) pattern to build React applications.
  • react-mini-router A minimal URL router mixin.
  • react-passage: Passage helps when linking or redirecting to routes that may or may not be in your react app.
  • react-router - A popular declarative router for React
  • react-router-component Declarative routing.

Routing

 

  • 'react-router-dom'

  • <BrowserRouter>

  • <Route path='/about' component={About}/>

  • <Link to='/'>Home</Link>

  • <NavLink to='/react' activeClassName='hurray'>React</NavLink>


     

Practice

Implement Router

 

Lesson 7

Agenda

  • Error Boundaries
  • React.memo
  • React.lazy
  • Suspense
  • Hooks

Error Boundaries

React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI

Catch

Do Not Catch

  • render
  • lifecycle
  • constructors
  • event handlers
  • async code
  • server side rendering
  • error in Error Boundary component

Error Boundaries

Render a fallback UI

Log error information

  • Only class components can be error boundaries.
  • Declare an error boundary component once and use it throughout your application
  • The granularity of error boundaries is up to you(minimum top-level routes)
  • Errors that were not caught by any error boundary will result in unmounting of the whole React component tree

React.memo

What is React.PureComponent?

class ListOfInts extends React.PureComponent {
  render() {
    return <div>{this.props.ints.join(",")}</div>;
  }
}
const ListOfInts = React.memo(props => <div>{this.props.ints.join(",")}</div>)
const ListOfInts = React.memo(props => <div>{this.props.ints.join(",")}</div>, areEqualFunction)

*areEqualFunction - should return reversed from shouldComponentUpdate

React.lazy

CommonJS

AMD

Ecma2015

Techniques for importing code in JS?

import('someOtherModule.js')
    .then(module => {
        module.loadPageInto(main);
    })
    .catch(err => {
        main.textContent = err.message;
    });

React.lazy

// Film.jsx

const Film = () => <div>Terminator</div>;

export default Film;
// App.jsx

import React, { lazy } from 'react';

const Film = lazy(() => import('./Film'));


class App extends React.Component {

 ...

  render() {
    return(
      <div>
        <h1>App with Films<h1>
        <Film>
      </div>
    );  
  }
}

React.Suspense

// App.jsx

import React, { lazy, Suspense } from 'react';

const Film = lazy(() => import('./Film'));


class App extends React.Component {

 ...

  render() {
    return(
      <div>
        <h1>App with Films<h1>
        <Suspense fallback={<h2>Films are loading<h2>}>
          <Film />
        </Suspense>
      </div>
    );  
  }
}

Concurrent React

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
ReactDOM.render(<App />, document.getElementById('root'));
<Suspense fallback={<h2>Films are loading<h2>} maxDuration='500'>
  <Film />
</Suspense>

Suspense example

class Suspense extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      showFallbackUI: true;
    }
  }

  componentDidCatch(error) {
    if (error instanceOf Promise) {
        error.then(() => this.setState({showFallbackUI: false}))
    }
  }

  render() {
    return this.state.showFallbackUI ? this.props.fallback : this.props.children;
  }

}

Hooooooks

Motivation

  • It’s hard to reuse stateful logic between components
  • Complex components become hard to understand
  • Classes confuse both people and machines
  • Functional Components could not use statefull logic

Using Hooks

  • Backward compatible
  • Gradually adopted
  • opt-in

Hook in action

class RandomInt extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      ints: [5, 3, 8]
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState({
      ints: [...this.state.ints, 
                parseInt(Math.random() * 10, 10)
        ];
    });
  }

  render() {
    return (
      <div>
        <button 
          onClick={this.handleClick}
        >
            Add Random Int
        </button>
        <ListOfInts ints={this.state.ints} />
      </div>
    );
  }
}
import { useState } from 'react';

function RandomInt() {
  const [ints, setInts] = useState([]);

  return(
    <div>
      <button 
        onClick={setInts([...ints, 
                parseInt(Math.random() * 10, 10)])}
      >
          Add Random Int
      </button>
    <ListOfInts ints={ints} />
  </div>
  )
}

Types Of Hooks

Built in

Custom

  • useState
  • useEffect
  • useContext
  • useReducer
  • useCallback
  • useMemo
  • useRef

a function that contains a stateful logic and is used in React functional component. (starts with 'use')

useEffect

useEffect(() => {
    googleMap.panTo(props.coordinates)
});

componentDidMount

componentDidUpdate

useEffect(() => {
    document.addEventListener('click', handleDocumentClick);

    return () => {
        document.removeEventListener('click', handleDocumentClick)
    }
});

componentDidMount

componentDidUpdate

componentWillUnmount

useEffect(() => {
    document.addEventListener(props.event, handleDocumentClick);

    return () => {
        document.removeEventListener(props.event, handleDocumentClick)
    }
}, [props.event]);

componentDidMount

conditional componentDidUpdate

componentWillUnmount

useEffect(() => {
    document.addEventListener(props.event, handleDocumentClick);

    return () => {
        document.removeEventListener(props.event, handleDocumentClick)
    }
}, []);

componentDidMount

componentWillUnmount

useContext

function Link(props) {
  const router = useContext(RouterContext);
    
  return() {
    <a
      href=""
      onClick={e => {
        e.preventDefault();
        router.pushState({}, "", props.to);
      }}
    >
      {props.children}
    </a>
  }
}
export default class Link extends React.Component {
  render() {
    return (
      <RouterContext.Consumer>
        {router => (
          <a
            href=""
            onClick={e => {
              e.preventDefault();
              router.pushState({}, "", this.props.to);
            }}
          >
            {this.props.children}
          </a>
        )}
      </RouterContext.Consumer>
    );
  }
}

useRef

function Map() {
  const googleMap = useRef(null);

  render() {
    return (
      <Map
	item
	xs={12}
	style={style}
	google={google}
	onReady={(gm) => googleMap.current = gm }
	onClick={(e) => googleMap.current.panTo(e.coordinates)}
	zoom={10}
      />
    )
  }
}
class Map extends React.Component {
  ready(googleMap) {
    this.map = googleMap;
  }

  onMapClick(e) {
    this.googleMap.panTo(e.coordinates);
  }

  render() {
    return (
      <Map
	item
	xs={12}
	style={style}
	google={google}
	onReady={this.ready}
	onClick={this.onMapClick}
	zoom={10}
      />
    )
  }
}

Rules Of Hooks

  • Applied to only functional components
  • Order of hooks calls matters
  • Do not call in loops, conditions, nested functions
  • Call hooks from top level of React component
  • Call hooks inside other hooks
  • Start your custom hook with 'use'

[GL ProCamp] React

By Viktor Shevchenko

[GL ProCamp] React

  • 1,424