0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React で Prop 変えれば再レンダーされないについて

Posted at

はじめに

最近音楽ストリーミングアプリの開発をやっています。
開発中、どうしても親コンポネントから子コンポネントに与えた prop を変えても変化が写ってないのところはかなり珍しかった。
ネットで調べみたら、原因は理解できました。

説明

まずはサンプルコードを見てみましょう。

import { useState } from 'react';

// 親コンポネント
const ParentComponent = () => {
    const [data, setData] = useState(0);
    
    return (
        <div>
            <ChildComponent prop={data} />

            <button onClick={() => setData(data => data + 1)}>Add +1</button>
        </div>
    )
}

// 子のコンポネント
const ChildComponent = ({ prop }: { prop: number }) => {
    return (
        <div>
            Prop: {prop}
        </div>
    )
}

親コンポネントでボタン押したら、data は変わっているから新たにChildComponentの prop の値は変わるでしょう?そういう考えたら、常識的にChildComponentの Prop 表示も変わると思うでしょう。でも、実際は変わらないです。

理由は、コンポネントのstateが変わるだけでそのコンポネントは再びレンダーされます。あくまでprop はコンポネントのstate じゃないから、再レンダーも行わないです。

対策方法は2つあります。

対策方法1

一番単純な対策法は、子コンポネントのkeypropを設定する。
コンポネントのkeyを変えれば、必ずコンポネントを再レンダーされますから。

import { useState } from 'react';

// 親コンポネント
const ParentComponent = () => {
    const [data, setData] = useState(0);
    
    return (
        <div>
            <ChildComponent key={data} prop={data} />

            <button onClick={() => setData(data => data + 1)}>Add +1</button>
        </div>
    )
}

// 子のコンポネント
const ChildComponent = ({ prop }: { prop: number }) => {
    return (
        <div>
            Prop: {prop}
        </div>
    )
}

対策方法2

この方法はちょっと見づらくて分かりづらいときがする。
メモする為書いておきますが、あまり勧めません。

propはコンポネント内でstateとして保管して、親コンポネントから受けたpropを変えたら、useEffectstateを更新すればコンポネントは再レンダーされます。

"use client"

import { useState, useEffect } from 'react';

// 親コンポネント
const ParentComponent = () => {
    const [data, setData] = useState(0);
    
    return (
        <div>
            <ChildComponent prop={data} />

            <button onClick={() => setData(data => data + 1)}>Add +1</button>
        </div>
    )
}

// 子のコンポネント
const ChildComponent = ({ prop }: { prop: number }) => {
    const [childData, setChildData] = useState(prop);

    useEffect(() => {
        setChildData(prop);
    }, [prop])
    
    return (
        <div>
            Prop: {childData}
        </div>
    )
}

見にくいでしょう?
そして、この方法にはもう1つの問題がある。
useEffectuseStateはクライントコンポネントしか使えないですので、Next.js などフレムワークで子コンポネントにサーバーコンポネントは使えなくなる。つまり、子コンポネントで非同期処理が必要になったら、その処理もuseEffectを使わず実行できなくなる。

宝個人的に対策法1を利用するのが楽だと思います。

最後に

React でpropを使うのが基礎ですが、この問題と遭うまでこんなことも起きられると思わなかった。
だからこそ、どれほど自信や経験持っても、自分でプロジェクトを作るのが大事ですよね。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?