反応:プロップが変更されても子コンポーネントが更新されない理由


135

次の疑似コードの例で、コンテナがfoo.barを変更したときに子が再レンダリングしないのはなぜですか?

Container {
  handleEvent() {
    this.props.foo.bar = 123
  },

  render() {
    return <Child bar={this.props.foo.bar} />
}

Child {
  render() {
    return <div>{this.props.bar}</div>
  }
}

forceUpdate()コンテナの値を変更した後に呼び出しても、子はまだ古い値を示しています。


5
これはあなたのコードですか?有効なReactコードではないようです

私は小道具値ではなく、それがSETSTATEによって親成分の変化である必要があり、その状態は、コンテナの小道具にマップする必要がありますコンテナコンポーネントに変更するべきではないと思う
Piyush Patelの

次のようなスプレッド演算子を使用します<Child bar = {... this.props.foo.bar} />
Sourav Singh

@AdrianWydmanskiと賛成した他の5人:en.wikipedia.org/wiki/Pseudocode
David Newcomb

@PiyushPatelプロップは、疑似コードの例が示すように、コンポーネントがインプレースで再レンダリングされるときに更新されます。これの別の例は、を使用するようなものです。<Route exact path="/user/:email" component={ListUserMessagePage} />同じページのリンクは、新しいインスタンスを作成して通常のライフサイクルイベントを実行することなく、小道具を更新します。
David Newcomb

回答:


94

名前と等しい属性「キー」を持つように子を更新します。コンポーネントは、キーが変更されるたびに再レンダリングします。

Child {
  render() {
    return <div key={this.props.bar}>{this.props.bar}</div>
  }
}

5
これをありがとう。私はreduxストアを使用していて、キーを追加するまでコンポーネントを再レンダリングできませんでした。(私はランダムなキーを生成するためにuuidライブラリを使用しました、そしてそれはうまくいきました)。
米谷

フックを使用すると、既存の配列に状態を設定するときに子が再レンダリングされません。私の(おそらく悪い考え)ハックは、ダミープロップの状態を同時に設定し、それをuseEffect依存関係配列に追加することでした: [props.notRenderingArr, props.dummy]。配列の長さが常に変化する場合は、useEffect依存配列をに設定できます[props.notRenderingArr.length]
深夜

5
これも私が必要としたものです。私は困惑しています、いつkey必要なのsetStateか、それとも単に?私は親の状態に依存している子供がいますが、彼らは再レンダリングをトリガーしていません...
dcsan

すばらしい答えです。しかし、なぜこのような振る舞いをするのでしょうか?
リシャフ

1
インデックスをキーとして使用していましたが、正しく機能していませんでした。今、私はuuidを使用していて、それは完璧です:) @Maiyaのおかげで
Ali Rehman

90

なぜなら、親のプロップが変更されても、その状態が変更された場合、子供は再レンダリングしないからです:)

あなたが示しているのはこれです:https : //facebook.github.io/react/tips/communicate-between-components.html

プロップを介して親から子にデータを渡しますが、そこには再レンダリングロジックはありません。

親にいくつかの状態を設定してから、親が状態を変更したときに子を再レンダリングする必要があります。これは役に立ちます。 https://facebook.github.io/react/tips/expose-component-functions.html


5
setStateによって再レンダリングが発生するのに、forceUpdateでは発生しないのはなぜですか?また、トピックから少し外れていますが、プロパティがreduxから渡され、その状態がアクションによって更新されるときに、コンポーネントはどのように更新されますか?
Tuomas Toivonen

コンポーネントを更新しても、渡された小道具はまだそこにありますが、小道具は更新されません。フラックスは、別のトピックのイエス:)ある
フランソワ・リチャード

3
state!=小道具について詳しく読む必要があります。小道具で更新を行わない
フランソワリチャード

ソースコードで直接確認できますが、同じプロセスではありません
Webwoman

そうあなたがここで言ったが、どちらか変更されていたか、私はそれを正しく理解していなかった、私は、途中でそのための質問をした stackoverflow.com/questions/58903921/...
ILoveReactAndNode

50

同じ問題がありました。これは私の解決策ですが、それが良い習慣かどうかはわかりませんが、そうでない場合は教えてください:

state = {
  value: this.props.value
};

componentDidUpdate(prevProps) {
  if(prevProps.value !== this.props.value) {
    this.setState({value: this.props.value});
  }
}

UPD:React Hooksを使用して同じことができるようになりました(コンポーネントが関数の場合のみ)。

const [value, setValue] = useState(propName);
// This will launch only if propName value has chaged.
useEffect(() => { setValue(propName) }, [propName]);

3
これは間違いなく正解です。最も単純なことは言うまでもありません
ギル

1
私もこのアプローチを使用しています。
rawsly

@ vancy-pants componentDidUpdateこの場合、Childコンポーネントに入ります。
Nick Friskel

4
プロップを子コンポーネントの状態に変換する必要はありません。小道具を直接使用するだけです。でFunctionComponents小道具の変更ならば、自動的に子コンポーネントを更新します。
oemera

1
このアプローチは、親状態から派生した子コンポーネントに小道具がある場合にも機能します
Chefk5

10

Reactの哲学コンポーネントによると、小道具を変更することはできません。それらは親から受け取り、不変でなければなりません。親だけが子の小道具を変更できます。

状態vs小道具についての良い説明

また、このスレッドを読んでください。どうしてreact.jsのプロップを更新できないのですか?


これは、コンテナーがreduxストアから受け取った小道具を変更できないことも意味しませんか?
Tuomas Toivonen

3
コンテナーはストアから直接小道具を受け取りません。それらは、redux状態ツリーの一部を読み取ることによって提供されます(混乱しますか??)。混乱は、react-reduxによって接続される魔法の関数について知らないためです。connectメソッドのソースコードが表示される場合、基本的には、storeStateプロパティを持つ状態を持ち、reduxストアにサブスクライブされる高次コンポーネントを作成します。storeStateが変更されると、再レンダリング全体が発生し、変更された状態を読み取ることにより、変更されたプロップがコンテナに提供されます。これが質問の答えになることを願っています
Sujan Thakare

良い説明、より高次のコンポーネントについて考えることは、何が起こっているのかを理解するのに役立ちます
Tuomas Toivonen

7

setState関数を使用する必要があります。そうでない場合、forceUpdateをどのように使用しても、状態は変更を保存しません。

Container {
    handleEvent= () => { // use arrow function
        //this.props.foo.bar = 123
        //You should use setState to set value like this:
        this.setState({foo: {bar: 123}});
    };

    render() {
        return <Child bar={this.state.foo.bar} />
    }
    Child {
        render() {
            return <div>{this.props.bar}</div>
        }
    }
}

コードが無効のようです。このコードはテストできません。


そうじゃないの<Child bar={this.state.foo.bar} />
Josh Broadhurst 2017

正しい。答えを更新します。しかし、私が言ったように、これは有効なコードではないかもしれません。質問からコピーするだけです。
JamesYin 2017

5

functionsおよびからReactコンポーネントを作成する場合useState

const [drawerState, setDrawerState] = useState(false);

const toggleDrawer = () => {
      // attempting to trigger re-render
      setDrawerState(!drawerState);
};

これはいない仕事を

         <Drawer
            drawerState={drawerState}
            toggleDrawer={toggleDrawer}
         />

これは機能します(キーを追加)

         <Drawer
            drawerState={drawerState}
            key={drawerState}
            toggleDrawer={toggleDrawer}
         />

3

確認済み、キーの追加は機能します。その理由を理解するためにドキュメントを読みました。

Reactは、子コンポーネントを作成するときに効率的であることを望んでいます。別の子と同じ場合、新しいコンポーネントをレンダリングしないため、ページの読み込みが速くなります。

キーを追加すると、Reactは新しいコンポーネントをレンダリングし、その新しいコンポーネントの状態をリセットします。

https://reactjs.org/docs/reconciliation.html#recursing-on-children


2

setState関数を使用します。だからあなたはできる

       this.setState({this.state.foo.bar:123}) 

ハンドルイベントメソッド内。

状態が更新されると、変更がトリガーされ、再レンダリングが行われます。


1
this.setState({foo.bar:123})である必要がありますか?
チャリスジャヤサンカ

0

状態を維持せず、単純に小道具をレンダリングして親から呼び出す場合は、子を機能コンポーネントとして作成する必要があります。これの代わりに、機能コンポーネント(useState)でフックを使用して、ステートレスコンポーネントを再レンダリングすることができます。

また、プロパスは不変なので、変更しないでください。コンポーネントの状態を維持します。

Child = ({bar}) => (bar);

0

私は同じ問題に遭遇していました。小道具Tooltipを受け取っているコンポーネントがあり、条件に基づいてコンポーネントをshowTooltip更新していました。コンポーネントで更新されていましたが、コンポーネントがレンダリングされていませんでした。ParentifParentTooltip

const Parent = () => {
   let showTooltip = false;
   if(....){ showTooltip = true; }
   return(
      <Tooltip showTooltip={showTooltip}></Tooltip>
   )
}

私がやっていた間違いはshowTooltip、letとして宣言することです。

レンダリングの動作原理に違反していることに気づき、フックで置き換えるとうまくいきました。

const [showTooltip, setShowTooltip] =  React.useState<boolean>(false);

0

子コンポーネントのconnectメソッドのmapStateToPropsで変更されたプロップを定義します。

function mapStateToProps(state) {
  return {
    chanelList: state.messaging.chanelList,
  };
}

export default connect(mapStateToProps)(ChannelItem);

私の場合、channelListのチャネルが更新されたので、mapStateToPropsにchanelListを追加しました

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.