今日から React 18 | 心を無にして始める React

React 18 の新機能をみたら、使いたくなったから今日から React 18 。

インストール

新しく npx create-react-app しましょう。
React 18 になります (/・ω・)/

package.json をのぞいてみます。

やったね!

動かしてみます。

cd ./project-name-react-18
npm run start

やったね!

せっかくなので気になる新機能を1つお試ししてみます。

と、その前に 最低限使いたいものを準備します。

準備

npm install react-bootstrap bootstrap

index.js を編集

import 'bootstrap/dist/css/bootstrap.min.css';

上の1行をぺたりと追加してこうなります。

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import 'bootstrap/dist/css/bootstrap.min.css';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

自動バッチング

Promise や setTimeout しても state の更新をまとめてやってくれるようになった!
REST API の応答をもらってから stateを更新する なんてのはよくありそう! (/・ω・)/

バッチングとは React がパフォーマンスのために複数のステート更新をグループ化して、単一の再レンダーにまとめることを指します。
自動バッチング以前は、React のイベントハンドラ内での更新のみバッチ処理されていました。
promise や setTimeout、ネイティブのイベントハンドラやその他あらゆるイベント内で起きる更新はデフォルトではバッチ処理されていませんでした。
自動バッチングにより、これらの更新も自動でバッチ処理されるようになります

https://ja.reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching から引用。

詳しい話は↑から。

局所的なサンプル

setTimeout とか promise で 複数のstateを更新 するので、こんな感じです。

const [state1, setState1] = useState("");
const [state2, setState2] = useState("");

const handleClick = () => {
  setTimeout(() => {
    setState1("newValue");
    setState2("newValue");
  }, 1000);
};

React 17 までの回避策

更新を 1回 にできるような state にする感じで、大変。

const [state, setState] = useState({
  state1: "",
  state2: "",
});

const handleClick = () => {
  setTimeout(() => {
    setState({
      state1: "newValue",
      state2: "newValue",
    });
  }, 1000);
};

一部だけ更新するときもスプレッド演算子を使ったりで大変 (/・ω・)/

const [state, setState] = useState({
  state1: "",
  state2: "",
});

const handleClick = () => {
  setTimeout(() => {
    setState({
      ...state,
      state1: "newValue",
    });
  }, 1000);
};

これからはこんなことしなくてよくなる~ (/・ω・)/

使ってみる

例によって App.js を編集します。

import './App.css';
import { Button } from 'react-bootstrap';
import { useState } from 'react';

function App() {
  console.log('render')

  const [state1, setState1] = useState("");
  const [state2, setState2] = useState("");

  const handleClick = () => {
    console.log("handleClick");
    setTimeout(() => {
      setState1("newValue");
      setState2("newValue");
    }, 1000);
  };

  return (
    <div className="App">
      <header className="App-header">
        <Button onClick={handleClick}>Button</Button>
      </header>
    </div>
  );
}

export default App;

このまま動かしてみる、といきたいところですが…。
React Strickmode をいったんコメントアウトするので、 index.js も編集します。

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import 'bootstrap/dist/css/bootstrap.min.css';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // <React.StrictMode>
    <App />
  // </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

それでは確認してみます。

参考までに、React 17 だったら 2回 呼ばれます。

React StrickMode

React StrickMode では、アプリケーションの潜在的な問題を検出するために render が2回呼ばれます。
開発中(npm run start して webpack-dev-server でみているとき)だけで、build したファイルではちゃんと1回になります。

具体的には、以下の関数が2回呼ばれます。

  • クラスコンポーネントの constructor, render, shouldComponentUpdate メソッド
  • クラスコンポーネントの getDerivedStateFromProps 静的メソッド
  • 関数コンポーネントの本体
  • state 更新用関数(setState の第 1 引数として渡されるもの)
  • useState, useMemo, useReducer に渡される関数

何もしなければ、今回は App コンポーネントが FC(関数コンポーネント) なので、開発中は 2回 呼ばれます。

今回は render の呼ばれる回数が大事な話なので、コンソールで確認するとき わかりやすい ようにコメントアウトしたということでした (/・ω・)/
普段はバグが早めに見つかって便利 ✨