Childlenなどの8つのレガシーReact APIが非推奨の理由

8つのレガシーReact APIとは

Reactにはreactパッケージからエクスポートされているが、新しく書くコードでの使用は推奨されていない、以下の8つのレガシーReact APIが存在します。

  1. Children
  2. cloneElement
  3. Component
  4. createElement
  5. createRef
  6. forwardRef
  7. isValidElement
  8. PureComponent

この記事ではなぜこれらの8つのレガシーReact APIが非推奨なのか、使うべきではないのかサンプルコード付きで解説します。

※ isValidElementのみReact公式は非推奨としていないが、個人的に非推奨です。

1. Children

Childrenはpropsであるchildrenから受け取ったJSXを操作および変換するために用います。

以下のようなコードの場合はchildrenを使用する際に、内側のコンポーネントのレンダー出力を取得する方法がなく、想定外の結果になる可能性があるため非推奨となっています。

App.tsx
import { Children } from 'react'
import type { ReactNode } from 'react'

type RowListProps = {
  children: ReactNode
}

function RowList({ children }: RowListProps) {
  return (
    <div className="RowList">
      {Children.map(children, (child) => (
        <div className="Row">{child}</div>
      ))}
    </div>
  )
}

function MoreRows() {
  return (
    <>
      <p>This is the second item.</p>
      <p>This is the third item.</p>
    </>
  )
}

function App() {
  return (
    <>
      <RowList>
        <p>This is the first item.</p>
        <MoreRows />
      </RowList>
    </>
  )
}

export default App

React Children sample ❌️

このようなコードの場合はChildrenを使用せずに、以下のようにChildrenを使わずに書くことが推奨されています。

App.tsx
import type { ReactNode } from 'react'

type Row = {
  id: number
  content: ReactNode
}

type RowListProps = {
  rows: Row[]
}

const data: Row[] = [
  { id: 1, content: <p>This is the first item.</p> },
  { id: 2, content: <p>This is the second item.</p> },
  { id: 3, content: <p>This is the third item.</p> },
]

function RowList({ rows }: RowListProps) {
  return (
    <div className="RowList">
      {rows.map((row) => (
        <div className="Row" key={row.id}>
          {row.content}
        </div>
      ))}
    </div>
  )
}

function App() {
  return (
    <>
      <RowList rows={data} />
    </>
  )
}

export default App

React Children sample ✅️

2. cloneElement

cloneElementは既存のReact要素を「コピーしつつ、一部のpropsやchildrenを差し替える」ためのAPIです。

Reactではパターン別の見た目を作りたい場合はpropsで変更するのが定石で、コピーする方法だとコードがわかりづらく、壊れやすいので非推奨となっています。

App.tsx
import { cloneElement } from 'react'

const Button = ({
  color,
  children,
}: {
  color: string
  children: React.ReactNode
}) => <button style={{ color }}>{children}</button>

function App() {
  const button = <Button color="red">OK</Button>

  const clonedButton = cloneElement(button, {
    color: 'blue',
  })

  return (
    <>
      <Button color="red">OK</Button>
      {clonedButton}
    </>
  )
}

export default App

React cloneElement sample ❌️

前述のコードの場合はcolorの値をpropsで渡したほうがわかりやすいです。

React cloneElement sample ✅️

App.tsx
const Button = ({
  color,
  children,
}: {
  color: string
  children: React.ReactNode
}) => <button style={{ color }}>{children}</button>

function App() {
  return (
    <>
      <Button color="red">OK</Button>
      <Button color="blue">OK</Button>
    </>
  )
}

export default App

3. Component

Reactは現在でもクラスコンポーネントをサポートしていますが、新しいコードでの使用は関数コンポーネントの使用が推奨されているため非推奨です。

App.tsx
import { Component } from 'react'

type Props = {
  name: string
}

class Greeting extends Component<Props> {
  render() {
    return <h1>Hello, {this.props.name}!</h1>
  }
}

function App() {
  return (
    <>
      <Greeting name="World" />
    </>
  )
}

export default App

React Component sample ❌️

関数コンポーネントの場合は以下のコードになります。

App.tsx
type Props = {
  name: string
}

const Greeting = ({ name }: Props) => <h1>Hello, {name}!</h1>

function App() {
  return (
    <>
      <Greeting name="World" />
    </>
  )
}

export default App

React Component sample ✅️

4. createElement

createElementによってReact要素を作成できます。

JSXを使用すれば簡単に書けるので、現在では非推奨になっています。

App.tsx
import { createElement } from 'react'

type Props = {
  name: string
}

function Greeting({ name }: Props) {
  return createElement('h1', { className: 'greeting' }, `Hello, ${name}!`)
}

function App() {
  return (
    <>
      <Greeting name="World" />
    </>
  )
}

export default App

React createElement sample ❌️

JSXを使用した場合はもっとシンプルになります。

App.tsx
type Props = {
  name: string
}

function Greeting({ name }: Props) {
  return <h1 className="greeting">Hello, {name}!</h1>
}

function App() {
  return (
    <>
      <Greeting name="World" />
    </>
  )
}

export default App

React Component sample ✅️

5. createRef

createRefはクラスコンポーネントで作成されますが、現在は関数コンポーネント向けのuseRefがあるため、createRefは非推奨です。

App.tsx
import { Component, createRef } from 'react'

type FormProps = {}

export default class App extends Component<FormProps> {
  inputRef = createRef<HTMLInputElement>()

  handleClick = () => {
    this.inputRef.current?.focus()
  }

  render() {
    return (
      <>
        <input ref={this.inputRef} />
        <button onClick={this.handleClick}>Focus the input</button>
      </>
    )
  }
}

React createRef sample ❌️

useRefの場合は以下の通り。

App.tsx
import { useRef } from 'react'

function App() {
  const inputRef = useRef<HTMLInputElement>(null)

  const handleClick = () => {
    inputRef.current?.focus()
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>Focus the input</button>
    </>
  )
}

export default App

React createRef sample ✅️

6. forwardRef

React 19以前はpropsからrefを取得できなかったので、forwardRefを使用する必要がありました。

しかし、React 19からはpropsからrefを取得可能になったため、forwardRefの使用は非推奨になりました。

App.tsx
import { forwardRef, useEffect, useRef } from 'react'

type Props = {
  children?: React.ReactNode
}

const Button = forwardRef<HTMLButtonElement, Props>(({ children }, ref) => {
  useEffect(() => {
    if (ref && typeof ref !== 'function') {
      alert(`ボタンのテキストは${ref.current?.textContent}です。`)
    }
  }, [])

  return <button ref={ref}>{children}</button>
})

function App() {
  const ref = useRef<HTMLButtonElement>(null)

  return (
    <>
      <Button ref={ref}>OK</Button>
    </>
  )
}

export default App

React forwardRef sample ❌️

React 19以降であれば、forwardRefを使用しない以下のコードで実行できます。

App.tsx
import { useEffect, useRef } from 'react'

type Props = {
  ref?: React.Ref<HTMLButtonElement>
  children?: React.ReactNode
}

function Button({ ref, children }: Props) {
  return <button ref={ref}>{children}</button>
}

function App() {
  const buttonRef = useRef<HTMLButtonElement>(null)

  useEffect(() => {
    alert(`ボタンのテキストは${buttonRef.current?.textContent}です。`)
  }, [])

  return (
    <>
      <Button ref={buttonRef}>OK</Button>
    </>
  )
}

export default App

React forwardRef sample ✅️

7. isValidElement

isValidElementは値がReact要素であるか判定します。

App.tsx
import { isValidElement, createElement } from 'react';

// ✅ JSX tags are React elements
console.log(isValidElement(<p />)); // true
console.log(isValidElement(<MyComponent />)); // true

// ❌ These are not React elements
console.log(isValidElement(null)); // false
console.log(isValidElement(25)); // false
console.log(isValidElement('Hello')); // false
console.log(isValidElement({ age: 42 })); // false

知名度が低くて何をするものなのかわかりづらいため、極力使わないことが望ましいです。

用途としてはSlotでJSXのReact要素のときだけ表示するなどが考えられます。

App.tsx
import { isValidElement, type ReactNode } from 'react'

type SlotProps = {
  children?: ReactNode
}

function Slot({ children }: SlotProps) {
  return <>{isValidElement(children) ? children : null}</>
}

const Hello = () => <h1>Hello</h1>

function App() {
  return (
    <>
      <Slot>
        <Hello />
      </Slot>
      <Slot>World!</Slot>
      <Slot />
    </>
  )
}

export default App

しかし、リテラル(文字列)の場合は表示させないケースというのは基本的にないので、通常はisValidElementではなく、「children !== null」で判定させます。

React isValidElement sample ❌️

8. PureComponent

PureComponentはComponentと似ていますが、同じpropsとstateに対しては再レンダリングをスキップします。

クラスコンポーネントなので、現在のReactでは非推奨です。

App.tsx
import { PureComponent } from 'react'

type GreetingProps = {
  name: string
}

class Greeting extends PureComponent<GreetingProps> {
  render() {
    const { name } = this.props
    return <h1>Hello, {name}!</h1>
  }
}

function App() {
  return (
    <>
      <Greeting name="World" />
    </>
  )
}

export default App

React PureComponent sample ❌️

関数コンポーネントの場合はmemoで囲むことで、PureComponentと同じ状態になります。

App.tsx
import { memo } from 'react'

type GreetingProps = {
  name: string
}

const Greeting = memo(({ name }: GreetingProps) => {
  return <h1>Hello, {name}!</h1>
})

function App() {
  return (
    <>
      <Greeting name="World" />
    </>
  )
}

export default App

React PureComponent sample ✅️

React Compilerが有効であればメモ化は自動化されるので、上記の対応は不要です。

React Compiler

まとめ

Reactでは以下の8つのレガシーReact APIが非推奨なので、もしコード内にあったら置き換えることをオススメします。

  1. Children
  2. cloneElement
  3. Component
  4. createElement
  5. createRef
  6. forwardRef
  7. isValidElement
  8. PureComponent

エディタ検索用正規表現

Children|cloneElement|Component|createElement|createRef|forwardRef|isValidElement|PureComponent