🙄

React19を触る前にReactの進化をおさらい!Todoリストを作って変遷を体験してみた

2024/12/31に公開

最初に

2024年12月5日にReact19の安定版がリリースされるなど、Reactはさまざまな進化をしてきています。
今回はTodoリストアプリを作成していき、それをおさらいしていきます。

この記事でやること

さまざまなversionのReactで同じアプリを作成していき、Reactの進化を体験していきます。

React15

使いやすさ: ★★☆☆☆
メリット: ★★★☆☆

アプリ作成

react15インストール

npm install react@15 react-dom@15

コード

クラスコンポーネントで書き、this.stateでの状態管理とthis.setStateで状態変更をします。

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


class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = { todos: [], input: '' };
    }


    add = () => {
        if (this.state.input === '') return;
        
        
        this.setState({
            todos: [...this.state.todos, this.state.input],
            input: ''
        });
    };
    
    
    change = (e) => {
        this.setState({
            input: e.target.value
        })
    }


    render() {
        return (
            <div>
                <input placeholder='TODO' value={this.state.input} onChange={this.change} />
                <button onClick={this.add}>Add</button>
                <ul>
                    {this.state.todos.map((todo, idx) => <li key={idx}>{todo}</li>)}
                </ul>
            </div>
        );
    }
}

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

感想

<>の構文が使えなかったり、今ではあまり使っていないクラスコンポーネント、hooksを使わない状態管理、ライフサイクルなど当時使えない機能などもあり、簡潔に書きにくい部分があると感じました。
また、エラーバウンダリーがないことによりエラーを手動でする必要があることやライフサイクルメソッドの複雑さ、thisの冗長さを感じました。

React16.7

使いやすさ: ★★★☆☆
メリット: ★★★☆☆

アプリ作成

react16.7インストール

npm install react@16.7 react-dom@16.7

コード

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


    static getDerivedStateFromError() {
        return { hasError: true };
    }
    
    
    componentDidCatch(error) {
        // ログなど
    }
    
    
    render() {
        if (this.state.hasError) {
            return <h1>Error</h1>;
        }
        return this.props.children;
    }
}


class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = { todos: [], input: '' };
    }
    
    
    add = () => {
        if (this.state.input === '') return;
    
    
        this.setState({
            todos: [...this.state.todos, this.state.input],
            input: ''
        });
    };
    
    
    change = (e) => {
        this.setState({
            input: e.target.value
        })
    }
    
    
    render() {
    
    
        return (
            <div>
                <input placeholder='TODO' value={this.state.input} onChange={this.change} />
                <button onClick={this.add}>Add</button>
                <ul>
                    {this.state.todos.map((todo, idx) => <li key={idx}>{todo}</li>)}
                </ul>
            </div>
        );
    }
}


function App() {
    return (
        <ErrorBoundary>
            <TodoApp />
        </ErrorBoundary>
    );
}

感想

ErrorBoundaryを使ってエラーハンドリングを追加しました。
レンダリング中、ライフサイクルでのエラーハンドリングができるようになりました。
適切に設計していけば、アプリ全体がクラッシュせず影響範囲を小さくできるのが良いかと思いました。
また、簡単なアプリだと実感できないですが、React16からはFiberアーキテクチャが採用されているので、パフォーマンスが向上されているかと思います。
またcontext apiも使用できます。

React16.8

使いやすさ: ★★★★☆
メリット: ★★★★☆

アプリ作成

インストール

npm install react@16 react-dom@16
コード
const TodoApp = () => {
    const [todos, setTodos] = React.useState([]);
    const [input, setInput] = React.useState('');
    
    
    const add = () => {
        if (input === '') return;
        
        
        setTodos([...todos, input]);
        setInput('');
    };
    
    
    const change = (e) => {
        setInput(e.target.value);
    }
    
    
    return (
        <div>
            <input placeholder='TODO' value={input} onChange={change} />
            <button onClick={add}>Add</button>
            <ul>
                {todos.map((todo, index) => <li key={index}>{todo}</li>)}
            </ul>
        </div>
    );
}

感想
クラスコンポーネントを関数コンポーネントとhooksを用いて置き換えたため、状態管理やライフサイクル部分のコードが簡潔になりました。
また、上記コードで使用されていたthisの記述もなくなり全体的に簡潔になりました。

React17

使いやすさ: ★★★★★
メリット: ★★★★☆

アプリ作成

インストール

npm install react@17 react-dom@17

コード

React16.8と変更なし

感想

React17は新機能がなく、コードに変更がないので開発体験は変化がないです。
新機能がなかったですが、段階的なアップグレードによるマイグレーションのしやすさが一つ特徴になります。
イベントシステムの改善などによりReactが複数バージョン共存可能になっています。

参考
https://legacy.reactjs.org/blog/2020/10/20/react-v17.html

React18

使いやすさ: ★★★★★
メリット: ★★★★★

アプリ作成

インストール

npm install react@18 react-dom@18

コード

const TodoApp = () => {
    const [todos, setTodos] = React.useState([]);
    const [input, setInput] = React.useState('');
    const [isPending, startTransition] = React.useTransition();
    
    
    const add = () => {
        if (input === '') return;
        startTransition(() => {
            setTodos([...todos, input]);
        });
        setInput('');
    };
    
    
    const change = (e) => {
        setInput(e.target.value);
    }
    
    
    return (
        <div>
            <input placeholder='TODO' value={input} onChange={change} />
            <button onClick={add}>Add</button>
            {isPending && <p>Updating...</p>}
            <ul>
                {todos.map((todo, index) => <li key={index}>{todo}</li>)}
            </ul>
        </div>
    );
}

感想

Concurrent Renderingが導入され、useTransition や useDeferredValueなどを使い、重い処理を後回しにしてパフォーマンスを上げることが可能になりました。
Automatic Batching、Suspenseなども導入され、ユーザー体験、パフォーマンス的にReact18を使うメリットが大きいと感じました。
React15の時と比べるとコードがかなり簡潔になっています。

まとめ

Reactはバージョンごとに開発しやすさ、パフォーマンス部分の進化を見ることができました。
React19など今後も動向を追っていきたいと思います。

Discussion