React Hook

In Deep

React Lifecycle

Redux Store

dispatch action

connect state

Redux Store

dispatch action

connect state

props / state lifecycle

Redux Store

dispatch action

connect state

props / state lifecycle

Entropy

Chaos

Function

Purely

Pure Function

Focus on

Input & Output

No timing-related state

variables you cared

(Reduce state machine in single component)

(useEffect / useCallback / useMemo)

(tick scoped state)

useState

import React, { useState } from 'react';

function ComponentIsMe() {
  const [count, setCount] = useState(0);

  return (
    <button
      onClick={() => setCount(count + 1)}
      type="button">
      Click to add!
    </button>
  );
}

useEffect

import React, { useState, useEffect } from 'react';

function ComponentIsMe() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Click ${count} times`;
  });

  return (
    <button
      onClick={() => setCount(count + 1)}
      type="button">
      Click to add!
    </button>
  );
}

Execute order

1. execute component function to get render results

(with state from useState)

2. call useEffect function after result merge into react tree

(check watch variables value between each render)

JS Scope

import React, { useState, useEffect } from 'react';

function ComponentIsMe() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setTimeout(() => {
      console.log(`Clicked ${count} 2 seconds ago`);
    }, 2000);
  });

  return (
    <button
      onClick={() => setCount(count + 1)}
      type="button">
      Click to add!
    </button>
  );
}

One time task

import React, { useState, useEffect } from 'react';

function ComponentIsMe() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setTimeout(() => {
      console.log(`Clicked ${count} 2 seconds ago`);
    }, 2000);
  }, [count]);

  useEffect(() => {
    const intvalId = setInterval(() => {
      console.log('Time is passing...');
    }, 1000);

    return () => clearInterval(intvalId);
  }, []);

  return (
    <button
      onClick={() => setCount(count + 1)}
      type="button">
      Click to add!
    </button>
  );
}

Honest always

import React, { useState, useEffect } from 'react';

function ComponentIsMe() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intvalId = setInterval(() => {
      console.log('Time is passing...');

      setCount(count + 1);
    }, 1000);

    return () => clearInterval(intvalId);
  }, [count]);

  return (
    <button
      onClick={() => setCount(count + 1)}
      type="button">
      Click to add!
    </button>
  );
}

Self Update

import React, { useState, useEffect } from 'react';

function ComponentIsMe() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intvalId = setInterval(() => {
      console.log('Time is passing...');

      setCount(c => c + 1);
    }, 1000);

    return () => clearInterval(intvalId);
  }, []);

  return (
    <button
      onClick={() => setCount(count + 1)}
      type="button">
      Click to add!
    </button>
  );
}

Mixin

import React, { useState, useEffect } from 'react';

function ComponentIsMe() {
  const [count, setCount] = useState(0);
  const [step, setStep] = useState(1);

  useEffect(() => {
    const intvalId = setInterval(() => {
      console.log('Time is passing...');

      setCount(c => c + step);
    }, 1000);

    return () => clearInterval(intvalId);
  }, [step]);

  return (
    <div>
      <span>{count}</span>
      <input
        type="text"
        value={step}
        onChange={e => setStep(Number(e.target.value){ />
    </div>
  );
}

useCallback

import React, { useState, useEffect, useCallback } from 'react';

function ComponentIsMe() {
  const [term, setTerm] = useState('');

  const getSearchURL = useCallback(() => `http://aa.com/search?term=${term}`, [term]);

  useEffect(() => {
    const data = fetch(getSearchURL());
  }, [getSearchURL]);

  // ... render
}

props ready

import React, { useState, useEffect, useCallback } from 'react';

function Parent() {
  const [term, setTerm] = useState('');

  const fetchData = useCallback(() => `http://aa.com/search?term=${term}`, [term]);

  return (
    <Child fetchData={fetchData} />
  );
}

function Child({ fetchData }) {
  const [data, setData] = useState(null);

  useEffect(async () => {
    const response = await fetchData();

    setData(response);
  }, [fetchData]);
}

Race condition

In class component

import React, { PureComponent } from 'react';

class Article extends PureComponent {
  state = {
    article: null
  };

  componentDidMount() {
    this.fetchData(this.props.id);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.id !== this.props.id) {
      this.fetchData(this.props.id);
    }
  }

  async fetchData(id) {
    const article = await API.fetchArticle(id);

    this.setState({ article });
  }

  // ...
}

Race Condition

Select Article Id = 1

Call API with ID = 1

Select Article Id = 2

Call API with ID = 2

API Res. ID = 1

API Res. ID = 2

select 2 but data is 1

In Hook

import React, { useState, useEffect } from 'react';

function Article({ id }) {
  const [article, setArticle] = useState(null);

  useEffect(() => {
    let cancelled = false;

    async function fetchArticle() {
      const response = await fetchArticleFromId(id);

      if (!cancelled) {
        setArticle(response);
      }
    }

    fetchArticle();

    return () => {
      cancelled = true;
    };
  }, [id]);

  // render ...
}

React Fiber

By Chia Yu Pai

React Fiber

  • 426