目次
8つのレガシーReact APIとは
Reactにはreactパッケージからエクスポートされているが、新しく書くコードでの使用は推奨されていない、以下の8つのレガシーReact APIが存在します。
- Children
- cloneElement
- Component
- createElement
- createRef
- forwardRef
- isValidElement
- PureComponent
この記事ではなぜこれらの8つのレガシーReact APIが非推奨なのか、使うべきではないのかサンプルコード付きで解説します。
※ isValidElementのみReact公式は非推奨としていないが、個人的に非推奨です。
1. Children
Childrenはpropsであるchildrenから受け取ったJSXを操作および変換するために用います。
以下のようなコードの場合はchildrenを使用する際に、内側のコンポーネントのレンダー出力を取得する方法がなく、想定外の結果になる可能性があるため非推奨となっています。
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このようなコードの場合はChildrenを使用せずに、以下のようにChildrenを使わずに書くことが推奨されています。
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 App2. cloneElement
cloneElementは既存のReact要素を「コピーしつつ、一部のpropsやchildrenを差し替える」ためのAPIです。
Reactではパターン別の見た目を作りたい場合はpropsで変更するのが定石で、コピーする方法だとコードがわかりづらく、壊れやすいので非推奨となっています。
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前述のコードの場合はcolorの値をpropsで渡したほうがわかりやすいです。
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 App3. Component
Reactは現在でもクラスコンポーネントをサポートしていますが、新しいコードでの使用は関数コンポーネントの使用が推奨されているため非推奨です。
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関数コンポーネントの場合は以下のコードになります。
type Props = {
name: string
}
const Greeting = ({ name }: Props) => <h1>Hello, {name}!</h1>
function App() {
return (
<>
<Greeting name="World" />
</>
)
}
export default App4. createElement
createElementによってReact要素を作成できます。
JSXを使用すれば簡単に書けるので、現在では非推奨になっています。
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 AppJSXを使用した場合はもっとシンプルになります。
type Props = {
name: string
}
function Greeting({ name }: Props) {
return <h1 className="greeting">Hello, {name}!</h1>
}
function App() {
return (
<>
<Greeting name="World" />
</>
)
}
export default App5. createRef
createRefはクラスコンポーネントで作成されますが、現在は関数コンポーネント向けのuseRefがあるため、createRefは非推奨です。
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>
</>
)
}
}useRefの場合は以下の通り。
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 App6. forwardRef
React 19以前はpropsからrefを取得できなかったので、forwardRefを使用する必要がありました。
しかし、React 19からはpropsからrefを取得可能になったため、forwardRefの使用は非推奨になりました。
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 AppReact 19以降であれば、forwardRefを使用しない以下のコードで実行できます。
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 App7. isValidElement
isValidElementは値がReact要素であるか判定します。
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要素のときだけ表示するなどが考えられます。
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では非推奨です。
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関数コンポーネントの場合はmemoで囲むことで、PureComponentと同じ状態になります。
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 AppReact Compilerが有効であればメモ化は自動化されるので、上記の対応は不要です。
まとめ
Reactでは以下の8つのレガシーReact APIが非推奨なので、もしコード内にあったら置き換えることをオススメします。
- Children
- cloneElement
- Component
- createElement
- createRef
- forwardRef
- isValidElement
- PureComponent
エディタ検索用正規表現
Children|cloneElement|Component|createElement|createRef|forwardRef|isValidElement|PureComponent
