質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.64%

子コンポーネントで発生したイベントによりstateを更新するときの構文

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 867

MakotoKato

score 10

 前提・実現したいこと

react nativeでアプリを制作しております。
基本的なところですが、子のコンポーネントから親のstateを変更したい場合の方法について
お知恵をいただけたらと思います

 発生している問題・エラーメッセージ

通常子のコンポーネントから親のステートを変更する場合、子のイベント(onClickやonSubmit)で親から渡ってきた関数を
実行する場合が多いかと思いますが、以下のような子のイベントを介さず、通常の関数実行のような形で親のステートを変更することは
可能なのでしょうか

 該当のソースコード

便宜上1つのソースコードに親と子のソースコードをまとめさせていただきました。
以下のようなイメージで親のステートを変更したく思います

class Parent extends Component {
constructor(props) {
  super(props);
  this.state = {
    token: null
  }

updateState(token){
  this.setState({token});
}

  render() {
    return (
      <Child parentMethod={(token) => this.updateState(token)} />
    );
  }
}

class Child extends Component {
  componentDidMount() {
    //getToken()は何かのトークンをとってくる関数と仮定
    let token = getToken()
    this.props.parentMethod(token);
  }
  render() {
    return null
  }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

Children がマウントされたときに、親から渡された parentMethod が実行されることによって、Parent の state が変更されて (Parentの)render が計2回呼ばれることが確認できます。

参考文献まで、ご紹介いただき目から鱗の思いです。
ありがとうございます!参考にさせていただきます!

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

こんにちは。
React&ReduxによるSPA開発の業務に直近の約一年ほど関わっており、現在も継続中の者です。

ご質問に

以下のような

として挙げられているコードについては

  • 子のイベントを介して親のステートを変更している。ただし、そのイベントは(例えば click のような)DOMイベントではなく、ライフサイクルイベントと呼ぶべきイベント

というのが回答になるかなと思います。以下、この回答について説明します。

ご質問のコードで、parentMethod が使われているのは Child の componentDidMount() の中の、以下の部分

componentDidMount() {
    //getToken()は何かのトークンをとってくる関数と仮定
    let token = getToken()
    this.props.parentMethod(token);
  }

ですが、これは

  • Childコンポーネントは、自身がマウントされたというイベントを契機に、Parentからpropsで渡される parentMethod を呼び出す。

と言えて、parentMethodは Parentのstateを変えるので、上記は「子のイベントを介して」親のステートを変更するコードとなっています。

componentDidMount() はライフサイクルメソッドのひとつですが、これを「コンポーネントがマウントされたというイベントによって呼ばれるメソッド」というように、イベントという言葉を使って理解しておくことが間違いではないということの根拠となる記事を挙げます。

上記は、Twitterのフォロワー数約2万の技術メンター Tyler McGinnis さんによる投稿ですが、タイトルにあるとおり、"Life Cycle Events" と、イベントという言葉を使っています。

この "ライフサイクルイベント" という言葉を借りれば、

  • 何らかの要素がクリックされたというイベントは DOMイベント 。コンポーネントがマウントされたというイベントはライフサイクルイベント。どちらもイベントであることには変わりない。従って、Parent は Children で発生したイベントによって、stateを変更される。

と、状況を整理することができます。

実際のコードで確認してみますと、以下はReactNativeアプリではなく、Webアプリを作る react-create-app で
作成される App.js を Parent.js にして、ご質問に挙げられているコードに、console.log 出力を何点か加えたものです。

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import Parent from './Parent';

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

Parent.js

import React, { Component } from 'react';

class Parent extends Component {

  constructor(props) {
    super(props);
    this.state = {
      token: null
    };
    this.renderingCount = 0;
  }

  updateState(token){
    console.log('Parent#updateState: token=' + token);
    this.setState({token});
  }

  render() {
    this.renderingCount ++;
    console.log(`renderingCount: ${this.renderingCount}`);
    console.log(this.state);
    return (
        <Child parentMethod={token => this.updateState(token)} />
    );
  }
}

class Child extends Component {

  componentDidMount() {
    //getToken()は何かのトークンをとってくる関数と仮定
    //let token = getToken()
    const token = 'yes';
    this.props.parentMethod(token);
  }

  render() {
    return <div>This is a child.</div>;
  }
}

export default Parent;

以下の画面キャプチャは上記のParentを表示させたところです。

イメージ説明

Children がマウントされたときに、親から渡された parentMethod が実行されることによって、Parent の state が変更されて (Parentの)render が計2回呼ばれることが確認できます。

 補足

上記のように初回の表示時に、2回 render() が走ってしまうことに対して、React の公式ドキュメントのcomponentDidMount()の説明に、以下のメモがあります。

You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state. Use this pattern with caution because it often causes performance issues. In most cases, you should be able to assign the initial state in the constructor() instead. It can, however, be necessary for cases like modals and tooltips when you need to measure a DOM node before rendering something that depends on its size or position.

  • 要約: componentDidMount()でsetState()するとrenderを2回呼ぶことになり、それはパフォーマンス上の問題になることがある。とはいえ、componentDidMount()でsetState()しなければならない場合もある。(モーダルやツールチップの表示など)

以上参考になれば幸いです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる