LoginSignup
0
0

More than 3 years have passed since last update.

[React] 動的に追加したコンポーネントのパラメータを変更しても更新されない

Last updated at Posted at 2019-05-06

note

React初心者です。

環境

  "dependencies": {
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-scripts": "3.0.0"
  },

概要

期待する動作

ボタンをクリックすると、時計が1つ増える。時計に表示する文字列は、各コンポーネントではなく、Appが管理する。時計は1秒ごとに更新される。

実装

下にあるコードを見たほうが早いと思いますが、簡単に説明。

Windowという名前のコンポーネントがある。props.textを表示する以外のことは何もしない。

Appは、textwindowsというステートを持っている。
textには、時刻を文字列にしたものが格納されていて、setIntervalで1秒ごとに更新される。
windowsは、Windowコンポーネントが格納された配列である。

画面上のボタンをクリックすると、state.windows に Windowコンポーネント を追加する。
Appのrenderの中にthis.state.windowsを表示する部分があるので、state.windows に要素を追加した時点で画面が更新されるはず。

起きる問題

動的に追加されたWindowたちの文字列が変化しない。

Appのrenderに直接書いたWindowは更新されているのが分かる。

codesandbox

念のため、ブログにもコードを記載します。

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

class Window extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <pre>{this.props.text}</pre>;
  }
}

class App extends React.Component {
  constructor(prop) {
    super(prop);
    this.state = {
      text: "",
      windows: []
    };
    setInterval(() => {
      this.updateText();
    }, 1000);
  }
  updateText() {
    this.setState({
      text: new Date(Date.now()).toString()
    });
  }
  addWindow() {
    this.setState({
      windows: this.state.windows.concat([<Window text={this.state.text} />])
      //windows: this.state.windows.concat([() => <Window text={this.state.text} />])
    });
  }
  render() {
    return (
      <div className="App">
        <Window text={this.state.text} />
        <hr />
        <div>
          {
            this.state.windows
            //this.state.windows.map(e => e())
          }
        </div>
        <hr />
        <div>
          <button
            onClick={() => {
              this.addWindow();
            }}
          >
            addWindow
          </button>
        </div>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

原因と解決策

上記のコードの、コメントアウト部分が、自分なりの解決策です。

下のように、domを直接配列に入れてしまっているのが間違い。おそらく、現時点でのthis.state.textを含むレンダリング済みのデータが格納されてしまっている。

    this.state.windows.concat([<Window text={this.state.text} />])

this.state.text が変化したら再構築されて欲しいはずなので、配列にはdomを返す無名関数を格納するようにして、

    this.state.windows.concat([() => {return (<Window text={this.state.text} />);}])

render() 側で無名関数を呼んで、domをレンダリングする。

        <div>
          { this.state.windows.map(e => e()) }
        </div>

これで期待通りの動作はするようになった。

原因と解決策(追記:2019/05/06)

stateにはコンポーネントを入れず、コンポーネントに渡す引数を保持させた方が良いとのコメントがありました。

この方法ならば、stateも最小限の大きさになり、可読性も上がりそうです。
配列にはキーワードや連想配列を格納するようにして、

    this.state.windows.concat(["Window"])

render() 側でキーワードに応じてレンダリングするdomを分岐する。

        <div>
          { this.state.windows.map(type => 
              (type == "Window") ? (<Window text={this.state.text} />) :
                                   (<p>{this.state.text}</p>))
          }
        </div>
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