#Reactで親コンポーネントで子コンポーネントを変更する

Reactで親コンポーネントで子コンポーネントを変更する方法を記載します。

#VNodeを変更する

下記のようにChildren.toArray()を使って、childrenをVNodeの配列に変換します。 isValidElement()でReact elementかどうかを判別します。 cloneElement()を使ってReact elementを変更します。

const root = createRoot(document.getElementById('app'));
                        
function Parent({children, word}) {
  const changedComponents = React.Children.toArray(children).map(c => {
    if (React.isValidElement(c)) {
      return React.cloneElement(c, {className: 'red'}, c.props.children + '2')
    }
    return c + word
  })
  return <div>{changedComponents}</div>
}

root.render(<Parent word="1">a<p>b</p>c</Parent>);

// a1
// b2
// c1

#子コンポーネント内の要素を参照する

下記のようにforwardRef()を使って、子コンポーネント内の要素を参照します。

import { forwardRef, useRef, useEffect } from 'react'

function _Child(_, ref) {
  return <p ref={ref}>foo</p>
}

const Child = forwardRef(_Child)

function Parent() {
  const ref = useRef(null)
  
  useEffect(() => {
    setTimeout(() => {
      ref.current.textContent = 'bar'}, 1000)
  }, [])
  
  return <Child ref={ref} />
}

#子コンポーネント内で定義された関数を実行する

下記のようにuseImperativeHandle()を使って、子コンポーネント内で定義された関数を実行します。

import { forwardRef, useRef, useEffect, useImperativeHandle } from 'react'

function _Child(_, ref) {
  const _ref = useRef(null)
  
  useImperativeHandle(ref, () => {
    return {
      changeText() {
        _ref.current.textContent = 'bar'
      },
    };
  }, []);

  return <p ref={_ref}>foo</p>
}

const Child = forwardRef(_Child)

function Parent() {
  const ref = useRef(null)
  
  useEffect(() => {
    setTimeout(() => {
      ref.current.changeText()
    }, 1000)
  }, [])
  
  return <Child ref={ref} />
}