warning: Can't call setState on a component that is not yet mounted

React開発中に出現したワーニングを対処しました。
ワーニングなのでそこまでクリティカルではないと思いますが備忘として残します。

調査

ワーニングメッセージを直訳すると、

まだマウントされていないコンポーネントsetState()は呼び出すことができません

のようになるだろうか。

さて、私が書いたコードは下記のものになります。

  constructor(props) {
    super(props);
    this.state = {
      memos: this.fetchAllMemo(),
      value: null,
    }
    this.setState({
      key: this.state.memos.length
    })
    this.handleClick = this.handleClick.bind(this);
  }

ワーニングメッセージから察するに、コンストラクタ内でsetStateをしているのがよろしくないということなのでしょう。

マウントした直後にstateを変更できる方法があれば良さそうですね。

修正方法

componentDidMountを使用します。*1
修正後のソースコードはこちらになります。

  constructor(props) {
    super(props);
    this.state = {
      memos: this.fetchAllMemo(),
      value: null,
    }
    this.handleClick = this.handleClick.bind(this);
  }

  //constructorで実施していたsetStateを切り出す
  componentDidMount() {
    this.setState({
      key: this.state.memos.length + 1
    })
  }

これでワーニングは解消されました。

念のため、公式ドキュメントも見てみることにします。
公式ドキュメントによれば、

componentDidMount() の中で、あなたはすぐに setState() を呼び出すことができます。それは余分なレンダーを引き起こしますが、ブラウザが画面を更新する前に起こります。これにより、この場合 render() が 2 回呼び出されても、ユーザには中間状態が表示されません。このパターンはパフォーマンス上の問題を引き起こすことが多いので、慎重に使用してください。

とのことです。なるほど、componentDidMount()内であればsetState()を呼び出すこともできるが、多少なりともパフォーマンスに影響が出る恐れがあるということなのですね。

そうすると私の解決方法はベストな選択ではないのかもしれませんね・・。

終わりに

前述した公式ドキュメントのページには、以下のような記述もあります。

このページには React コンポーネントクラス定義の詳細な API リファレンスがあります。また、あなたが コンポーネントや props などの基本的な React の概念、および state やライフサイクルに精通していることを前提としています。

はい、精通していません・・。
コンポーネントのライフサイクル等、勉強すべきことはまだまだ多いですね。

日々精進。

*1:方法は他にもあるかもしれませんが・・