【React 入門】Reactの基礎から再レンダリングの最適化まで

React

今回は、React のJSX記法やコンポーネントなど基礎的な文法から、
React の仕様である再レンダリングに関する注意点など見ていきたいと思います。

なお、すでに React プロジェクトを作成している前提とします。

JSX記法

まずは何もない状態から始めたいので、
「/src」のApp.css と App.js の中身を削除し(ファイル自体は残しててOKです)、
index.js を以下のように書き換えます。

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

const App = () => {
    return (
        <h1>Hello world</h1>
    );
};

ReactDom.render(
    <App />, 
    document.getElementById('root')
);


ご覧のように、JavaScript の記述の中に HTML を書くような記法をJSXと言います。

注意点として、複数のタグを使用する場合は全体をタグ等で囲む必要があります。



ダメなパターン↓

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

const App = () => {
    return (
        <h1>Hello world</h1>
        <p>こんにちは世界</p>
    );
};

ReactDom.render(
    <App />, 
    document.getElementById('root')
);


OKパターン↓

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

const App = () => {
    return (
        <div>
            <h1>Hello world</h1>
            <p>こんにちは世界</p>
        </div>
    );
};

ReactDom.render(
    <App />, 
    document.getElementById('root')
);


div も不要な場合は↓

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

const App = () => {
    return (
        <>
            <h1>Hello world</h1>
            <p>こんにちは世界</p>
        </>
    );
};

ReactDom.render(
    <App />, 
    document.getElementById('root')
);


ブラウザの表示↓

コンポーネント化

今は index.js に直接HTMLを記入しましたが、次は別のファイルにコンポーネントとして記述し、それを index.js に読み込むという形にしてみます。

では App.js の中身を以下のようにします。

import React from 'react';

const App = () => {
    return (
        <div>
            <h1>Hello world</h1>
            <p>こんにちは世界</p>
        </div>
    )
}

export default App;


export default 変数名 とすることで、他のファイルでも読み込むことができるようになります。


では index.js を以下のように修正します。

import React from 'react';
import ReactDom from 'react-dom';
import App from './App'

ReactDom.render(
    <App />, 
    document.getElementById('root')
);


これでブラウザの表示は先ほどと同様になります。



ちなみに、export のやり方は

export const XXX = () => {
    ...
}

とすることもでき、この場合の import は

import { XXX } from 'パス'

となります。

イベントの扱い

たとえば、button 要素にクリックイベントを付与したいときは、
onClick={ JavaScriptの記述 } となります。

import React from 'react';

const App = () => {
    const showAlert = () => alert();
    
    return (
        <div>
            <h1>Hello world</h1>
            <p>こんにちは世界</p>
            <button onClick={showAlert}>クリック</button>
        </div>
    )
}

export default App;


上の例では、showAlert という関数を定義し、onClick で呼び出しています。
(button をクリックするとアラートが表示されます。)

スタイルの扱い

イベントと同様、スタイルを当てたい要素に
style={ JavaScriptの記述 } と書きます。

import React from 'react';

const App = () => {
    const showAlert = () => alert();
    const btnStyle = {
        color: 'white',
        backgroundColor: 'blue',
        padding: '4px 10px',
        borderRadius: '5px'
    };
    
    return (
        <div>
            <h1>Hello world</h1>
            <p>こんにちは世界</p>
            <button style={btnStyle} onClick={showAlert}>クリック</button>
        </div>
    )
}

export default App;


注意点としては、JavaScript のオブジェクト形式で書く必要があるので、
普通のCSSでは「background-color: blue」と書くところを、
「backgroundColor: ‘blue’」のように書きます。


ブラウザの表示↓

コンポーネントにプロパティを付与

このセクションの完成形を先にみせると以下のようになります。


今回新しく追加した二行の異なっている部分は、

・あいさつ
・名前
・文字色

となっています。

なので、それぞれの一行をPerson コンポーネントとし、
プロパティを与えることで管理する方法をみていきます。

まず「/src」のなかに「/src/components」「/src/components/Person.js」を作ります。


そして App.js を修正します。

import React from 'react';
import Person from './components/Person';

const App = () => {
    const showAlert = () => alert();
    const btnStyle = {
        color: 'white',
        backgroundColor: 'blue',
        padding: '4px 10px',
        borderRadius: '5px'
    };
    
    return (
        <div>
            <h1>Hello world</h1>
            <Person color="blue" greeting="こんにちは" name="青嶋" />
            <Person color="red" greeting="ごきげんよう" name="赤城" />
            <button style={btnStyle} onClick={showAlert}>クリック</button>
        </div>
    )
}

export default App;


まず、

import Person from './components/Person';

で Person コンポーネントを読み込み、

<Person color="blue" greeting="こんにちは" name="青嶋" />
<Person color="red" greeting="ごきげんよう" name="赤城" />

でプロパティをコンポーネントに渡しています。



そして Person.js を以下のように書きます。

import React from 'react';

const Person = (props) => {
    const { color, greeting, name } = props;
    const personStyle = {
        color: color,
    }

    return <p style={personStyle}>{greeting}、{name}です</p>
};

export default Person;

props.プロパティ名とすることで値を受け取ることができます。

ちなみに、

const { color, greeting, name } = props;

は分割代入とよばれるもので、以下と同じになります。

const color = props.color;
const greeting = props.greeting;
const name = props.name;





また、コンポーネントにプロパティとして渡すテキストなどを、
HTMLの中身のような感覚で渡すこともできます。
(ブラウザの表示は同様)

import React from 'react';
import Person from './components/Person';

const App = () => {
    const showAlert = () => alert();
    const btnStyle = {
        color: 'white',
        backgroundColor: 'blue',
        padding: '4px 10px',
        borderRadius: '5px'
    };
    
    return (
        <div>
            <h1>Hello world</h1>
            <Person color="blue">
                こんにちは、青嶋です
            </Person>
            <Person color="red">
                こんにちは、青嶋です
            </Person>
            <button style={btnStyle} onClick={showAlert}>クリック</button>
        </div>
    )
}

export default App;
import React from 'react';

const Person = (props) => {
    const { color, children } = props;
    const personStyle = {
        color: color,
    }

    return <p style={personStyle}>{children}</p>
};

export default Person;

props.children でタグの中のコンテンツを表示することができます。

useState

State とは、各コンポーネントが持つ状態のことであり、
useState を使うことで状態を保持することができます。

今回作るのは以下のようなカウンターです。

import React, { useState } from 'react';

const App = () => {
    const [num, setNum] = useState(0);
    const countUp = () => {
        setNum(num + 1);
    };
    
    const btnStyle = {
        color: 'white',
        backgroundColor: 'blue',
        padding: '4px 10px',
        borderRadius: '5px'
    };
    
    return (
        <div>
            <button style={btnStyle} onClick={countUp}>クリック</button>
            <p>{num}</p>
        </div>
    )
}

export default App;


まず以下の部分ですが、

const [num, setNum] = useState(0);

[変数名, 変数を更新するための関数名] = useState(初期値);

という形式で定義し、
ボタンをクリックすると countUp -> setNum と呼び出され、num が +1 されます。



次に、偶数・奇数判定をしてみます。

import React, { useState } from 'react';

const App = () => {
    const [num, setNum] = useState(0);
    const [evenFlag, setEvenFlag] = useState(true);

    const countUp = () => {
        setNum(num + 1);
        setEvenFlag((num + 1) % 2 === 0 ? true: false);
    };
    
    
    const btnStyle = {
        color: 'white',
        backgroundColor: 'blue',
        padding: '4px 10px',
        borderRadius: '5px'
    };
    
    return (
        <div>
            <button style={btnStyle} onClick={countUp}>クリック</button>
            <p>{num}</p>
            <p>
                {evenFlag && '偶数です'}
                {evenFlag || '奇数です'}
            </p>
        </div>
    )
}

export default App;

useEffect

たとえば、以下のようなボタンを押したら flag を true に変えたい処理があるとします。

import React, { useState } from 'react';

const App = () => {
    const num = 5;
    const [flag, setFlag] = useState(false);
    const setFlagTrue = () => {
        setFlag(true);
    };

    if(num > 0) {
        flag && setFlag(false);
    }

    return <button onClick={setFlagTrue}>クリック</button>
}

export default App;


しかし、

    if(num > 0) {
        flag && setFlag(false);
    }

という処理のせいで、必ず false になってしまいます。

そのようなときに、この処理のタイミングを num が更新されたときだけに絞りたいので、
useEffect を使います。

useEffect(() => { 処理 }, 監視対象の変数の配列)

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

const App = () => {
    const num = 5;
    const [flag, setFlag] = useState(false);
    const setFlagTrue = () => {
        setFlag(true);
    };

    useEffect(() => {
        if(num > 0) {
            flag && setFlag(false);
        }
    }, [num]);
    
    return <button onClick={setFlagTrue}>クリック</button>
}

export default App;

これにより、第2引数に指定した num が更新されなければこの処理はスルーされます。

ちなみに第2引数を空の配列にすると、最初にレンダリングされたときだけ処理させることができます。

再レンダリングの最適化

再レンダリングは以下のようなタイミングにおこります。

・state, props が更新されたとき
・親コンポーネントが再レンダリングされたとき


レンダリング処理を軽くするために、再レンダリングを最適化する必要があります。

memo

親コンポーネントが再レンダリングされても子コンポーネントのpropsに変更がなく、再レンダリングする必要がない場合は、
子コンポーネントを memo() で囲みます。

import { memo } from "react";

export const 子コンポーネント = memo((props) => {
    ...
});

useCallback

子コンポーネントに与えているプロパティがアロー関数であるとき、その関数が実行されるとプロパティが変更されたと判断され再レンダリングが起きてしまいます。
(memo() していてもダメ)

const arrowFunc = () => ...;

return (
    <子コンポーネント onClick={arrowFunc] />
)

これはアロー関数は呼び出されるたびに毎回生成されるのが原因のようです。

これを解消するため、useCallback() でアロー関数を囲みます。

useCallback(() => { 処理 }, 監視対象の変数の配列)

const arrowFunc = useCallback(() => ..., []);

return (
    <子コンポーネント onClick={arrowFunc} />
)

これにより、第2引数で指定した変数が変更したときだけこの関数を再生成します。
(つまり子コンポーネントは再レンダリングしない)

したがって第2引数を空の配列にすると、最初に生成した関数を使いまわすということになります。




今回は以上になります。
ご覧いただきありがとうございました!

続きはこちら↓

コメント

コンタクトフォーム

    タイトルとURLをコピーしました