Reactのrefを理解する@Typescript

https://reactjs.org/docs/refs-and-the-dom.html が微妙によくわからなかったので、refを使用していない場合と使用した場合の違いを確かめたメモ。確か、`ref`はreduxのtodolist exampleに唐突に出てきて[^redux]、「えっ」ってなってしまった人もいそう。
上記のリンクの例題に即しています。

[^redux]: https://redux.js.org/docs/basics/UsageWithReact.html#containersaddtodojs のこと。

問題設定としては、ボタンと入力フォームがあり、ボタンをクリックすると、入力フォームにフォーカスが移るようにしたいというものです。

環境構築周りは、https://www.typescriptlang.org/docs/handbook/react-&-webpack.html あたりを参考に。

## 準備

本題で、コンポーネントである`CustomTextInput.tsx`を用意するのだが、ここでは必要なファイルを準備。

```typescript:src/components/App.tsx
import * as React from 'react';
import CustomTextInput from './CustomTextInput';

function app(): JSX.Element {
  return (
    <div>
      <CustomTextInput />
    </div>
  );
}

export default app;
```

```typescript:index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';

import App from './components/App';

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

index.htmlは、https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html に従って用意。

# state

ただ単に、submit時に文字を受け取るだけならば、refを使う必要はない。

```typescript:src/components/CustomTextInput.tsx
import * as React from "react";

class CustomTextInput extends React.Component<{}, {textInput: string}> {
  constructor(props: {}) {
    super(props);
    this.state = {
      textInput: '', // if undefined or null, error
    };
  }

  handleChange = (e: React.FormEvent<HTMLInputElement>): void => {
    this.setState(
      {textInput: e.currentTarget.value}
    );
  }

  handleTextSubmit = (e: React.FormEvent<HTMLInputElement>): void => {
    alert(this.state.textInput);
  }
  render() {
    return (
      <div>
        <input 
          // 'string | number | string[]'
          value={this.state.textInput}
          onChange={this.handleChange} 
        />
        <input
          type="button"
          value="the text input"
          onClick={this.handleTextSubmit}
        />
      </div>
    );
  }
}

export default CustomTextInput;
```

textInputは`string`型だが、focusしたいときは`HTMLElement`型である必要がある。しかしながら、inputタグのvalue attributeでは、`string | number | string[]`型しか受け付けないので、ボタンを押してもfocusできない状況である。
ちなみに`handleTextSubmit`とか`handleChange`はmethodでなく、function proptetyになっている.
methodにすると、イベントハンドラー配下の`this`キーワードがオブジェクトインスタンスでなく、イベントの要素自体を示してしまい、undefinedとなってしまうためである[^event]。

[^event]: Javascript本格入門改訂新版 6.7節 p.352の解説がわかりやすい。Typescriptでは、function proptetyの`this`は意図通り、CustomTextInputのインスタンス自体を指す。詳しくは、https://www.typescriptlang.org/docs/handbook/functions.html の`this parameters in callbacks`の節を見れば良い。

# ref

で、本題なんだけど、inputタグでref attributeを利用して、このように書けば良い：

```typescript:src/components/CustomTextInput.tsx
import * as React from "react";

class CustomTextInput extends React.Component<{}, {}> {
  // Once the component mounts, 
  // React will call the ref callback with the DOM element, and will call it with null when it unmounts.
  // see also https://stackoverflow.com/questions/33796267/how-to-use-refs-in-react-with-typescript
  private textInput: HTMLInputElement;
  constructor(props: {}) {
    super(props);
  }

  focusTextInput = (): void => {
    // Explicitly focus the text input using the raw DOM API
    this.textInput.focus();
  }

  render() {
    // Use the `ref` callback to store a reference to the text input DOM
    // element in an instance field (for example, this.textInput).
    return (
      <div>
        <input
          type="text"
          ref={(input: HTMLInputElement) => { this.textInput = input } }
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

export default CustomTextInput;
```

refの右辺はクロージャーになっているが、inputの型が`HTMLElement`のサブクラスであることに注意する。なので、`focus`メソッドが使えるわけだ。

注意点としては、Typescriptでは型を明記する必要があるので、[Refs and the Dom](https://reactjs.org/docs/refs-and-the-dom.html)のコードをそのまま拝借しても型不一致でコンパイルに失敗する。

こういうときは、`private textInput: HTMLInputElement;`とするとうまくいく[^private]。

[^private]: https://stackoverflow.com/questions/33796267/how-to-use-refs-in-react-with-typescript　を参照

普通はrefは多用すべきでないことに留意する[^not_overuse]。(といっても、シンプルなものに関しては、ref使って良いんじゃない、っていうのをhttps://reactjs.org/docs/uncontrolled-components.html 内のリンク [this article on controlled versus uncontrolled inputs](https://goshakkk.name/controlled-vs-uncontrolled-inputs-react/)で言っていたりする)

[^not_overuse]: https://reactjs.org/docs/refs-and-the-dom.html#dont-overuse-refs