React で Stateless function components cannot be given refs. という警告がでた

Material-UI を使っていて以下のように書いたところ、警告が出た。

<TextField
  ref={(element) => this.textField = element}
  label="ID"
  className={classes.textField}
  value={name}
  margin="normal"/>
Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.

そもそも ref ってよくわかっていない

Refs provide a way to access DOM nodes or React elements created in the render method.

render() で作った DOM node や React elements にアクセスするための方法を提供してくれるとのこと。

String Refs

これは deprecated (廃止予定) なので使わないほうが良い。
https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs

class StringRefs extends React.Component {

  handleClick = () => {
    console.log(this.refs.inputEl.value) // aaa
  }

  render() {
    return (
      <div>
        <input type="text" ref="inputEl" defaultValue="aaa" />
        <button type="button" onClick={this.handleClick}/>
      </div>
    )
  }
}

Callback Refs

これが現状主流なのかな?

https://reactjs.org/docs/refs-and-the-dom.html#callback-refs

class CallbackRefs extends React.Component {

  handleClick = () => {
    console.log(this.inputEl.value) // aaa
  }

  render() {
    return (
      <div>
        <input type="text" 
             ref={(element) => {
               this.inputEl = element
             }}
             defaultValue="aaa" />
        <button type="button" onClick={this.handleClick}/>
      </div>
    )
  }
}

Creating Refs

React v16.3 から使えるようになった方法。こっちの方がわかりやすいかも。

class CreatingRefs extends React.Component {

  inputEl = React.createRef()

  handleClick = () => {
    console.log(this.inputEl.current.value)  // aaa
  }

  render() {
    return (
      <input type="text" ref={this.inputEl} defaultValue="aaa"/>
      <button type="button" onClick={this.handleClick}/>
    )
  }
}

本題に戻る

Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.

Stateless function components っていうのは引数で渡ってきた props 以外は使えないコンポーネント(実質ただの関数)っぽい。

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}

こんなノリにすると子コンポーネントの input の ref を親コンポーネントで扱えるらしい

https://material-ui.com/api/text-field/

を見てみたらまったく同じプロパティあるやん!というわけで、

<TextField
  inputRef={(element) => this.textField = element}
  label="ID"
  className={classes.textField}
  value={name}
  margin="normal"/>

で警告が消えた。