これからはFunction Componentですべて解決できる――というのはどうやら幻想だったようです。

## 何がしたかったのか
Reactには、Lazy Componentというものがあります。

```tsx:MyComponent.tsx
import React, { FC } from 'react';

const MyComponent: FC = () => (
  <div>Hello LazyComponent!</div>
);

export default MyComponent;
```

```tsx:MyApp.tsx
import React, { FC, Suspense, lazy } from 'react';

const MyComponent = lazy(() => import('./MyComponent'));

const MyApp: FC = () => (
  <div>
    <Suspense fallback={<div>Loading...</div>}>
      <MyComponent />
    </Suspense>
  </div>
);

export default MyApp;
```
とすると、MyComponentのロードが完了するまでfallbackに設定された`<div>Loading...</div>`を代わりにレンダリングしてくれるというものです。

で、いろいろ調べてたらこんなこともできると判明。
> ```jsx:LazyComponent.js
> import React, { Component } from 'react';
> 
> let result = null;
> const timeout = (msec) => new Promise(resolve => {
>   setTimeout(resolve, msec)
> });
> 
> const LazyComponent = () => {
>   if (result !== null) {
>     return (
>       <div>{result}</div>
>     )
>   }
>   throw new Promise(async(resolve) => {
>     await timeout(1000);
>     result = 'Done'
>     resolve();
>   })
> };
> 
> export default LazyComponent;
> ```
> [React 16.6の新機能、React.lazyとReact.Suspense を使った非同期処理](https://qiita.com/tatakahashiap/items/6ef427f3d0e55371113e#%E5%85%A8%E4%BD%93%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89)

こう書いたら、throwしたPromiseがresolveされたときにもう1回レンダリングされるらしく。私の探し方が悪いのか何なのか、この仕様はReactのドキュメント上で見つけることができませんでした。どこに書いてあるのか知っている人がいたらこっそり教えてほしいです。

それはさておきこの仕様、ドキュメントで見つからなかったので動かない前提で試しに書いてみました。

```tsx:試しに書いたコード
import React, { FC, lazy, Suspense } from 'react';

const PromiseTest= lazy(async () => {
  let state = 0;
  const TestInner: FC = () => {
    if(state) {
      return (
        <div>Done! {state}</div>
      )
    }
    throw new Promise((res) => {
      setTimeout(() => {
        state = 5;
        res();
      }, 5000);
    });
  };
  return {
    default: TestInner,
  };
});

const TestApp: FC = () => {
  return (
    <div>
      <Suspense fallback={<div>WAITING...</div>}>
        <PromiseTest />
      </Suspense>
    </div>
  );
}
```
やってみた結果……動く！動くぞ！

## さて、問題のコードに移ろうじゃないか
さて、Promiseをthrowしたら期待通りに動くことが分かったんですけれど。`state = 5`ってPromiseの中で変数に代入しちゃってるじゃないですか。ぶっちゃけキモいですよね。
`useState`フックに置き換えてもいけるんじゃね？って思った私、置き換えてみました。

```tsx:置き換えてみた
import React, { FC, lazy, Suspense, useState } from 'react';

const PromiseTest= lazy(async () => {
  const TestInner: FC = () => {
    const [state, setter] = useState(0);
    if(state) {
      return (
        <div>Done! {state}</div>
      )
    }
    throw new Promise((res) => {
      setTimeout(() => {
        setter(5);
        res();
      }, 5000);
    });
  };
  return {
    default: TestInner,
  };
});

const TestApp: FC = () => {
  return (
    <div>
      <Suspense fallback={<div>WAITING...</div>}>
        <PromiseTest />
      </Suspense>
    </div>
  );
}
```
あれ、動かん:thinking::thinking::thinking:
動かんぞ。

`useState`に置き換える前は動いたコードが、置き換えた瞬間動かなくなりました。てゆうか、`setter`は普通に呼ばれているはずなのに、`state`の値は0のまま。なんでや、、、。

## 諦めてComponent classにしてみた
というわけで、`PromiseTest`の実装をComponent classに変えてみました。`state`を`this.state.state`、`setter`を`this.setState`に変えただけですけどね。

```tsx:classに書き換えてみた
class TestInner extends React.Component<{}, { state: number }> {
  constructor(props: {}) {
    super(props);

    this.state = {
      state: 0,
    };
  }

  render() {
    if(this.state.state) {
      return (
        <li>Done! {this.state.state}</li>
      );
    }
    throw new Promise((res) => {
      setTimeout(() => {
        console.log('resolved');
        this.setState({ state: 5 });
        res();
      }, 5000);
    });
  }
}

const PromiseTest = lazy(async () => {
  return {
    default: TestInner,
  };
});

const TestApp: FC = () => {
  return (
    <div className='board-list-container'>
      <Suspense fallback={<div>WAITING...</div>}>
        <PromiseTest />
      </Suspense>
    </div>
  );
}
```
こうすると、動きました。動いてしまいました。え、なんでなんや、、、:thinking:
……Function Componentが使えない極めてまれなケースの1つを発見した身としては、非常に頭が痛いです。こういう重要なことはもっとわかりやすくドキュメントに書いておいて下せぇ……。

## 結論
***React、なんもわからん。***