ちゃんなるぶろぐ

エンジニア5年生🧑‍💻 オライリーとにらめっこする毎日。

【5分でわかる!】DOM構成に全く影響を与えずにReactコンポーネントを実行する

どうも、ちゃんなるです!

今回はReactに関してです(ソースコード on Github)。

概要

今回の内容は実務で使うことは少ないかもしれません。「そんな考え方もあるんやな」程度に聞いてください。

今回やりたいこと

DOM構成を変えることなくReactコンポーネント内の処理を実行させる。

(あれReactって画面を作るためのフレームワークやないっけ?本末転倒やん。。。という指摘は目を瞑ってください笑)

実現方法

DOM要素を新しく作り、それを起点にReactのレンダラーを実行する。

すなわち、ReactDOM.createRoot()に画面表示用のHTMLファイルに記述されてないDOM要素を渡してあげる。

下記の例ではcreateElement()tentativeElementというdiv要素を新規作成し、それに対してレンダリングをしています。

// index.jsx

import ReactDOM from 'react-dom/client'
import { App } from './App'

const tentativeElement = document.createElement('div')
const tentativeRoot = ReactDOM.createRoot(tentativeElement)
tentativeRoot.render(
  <App />
)

これにより、例えば下記のようにAppコンポーネント内の任意の処理を実行できます👍

// App.jsx

export const App = () => {
  /*任意の処理を記述する*/
  console.log("Since the DOM element we're rendering to isn't visible anywhere, ")
  console.log("returning a JSX element here won't be rendered.")

  /*ここでJSXを返しても良いが、どのみちレンダリングされないのでnullとしました。*/
  return null
}

Reactコンポーネント内部なのでReact固有の処理、例えばReactのHooks*1が使えます👍

もちろんreturnの際に他の関数コンポーネントを返すこともできるので、複数の関数コンポーネント内の処理も行えます。

// App.jsx

export const App = () => {
  /*任意の処理を記述する*/

  return (
    <Contents1 />
    <Contents2 />
    <Contents3 />
  )
}

const Contents1 = () => { ... }
const Contents2 = () => { ... }
const Contents3 = () => { ... }

まとめ

ブラウザ上に表示されるDOM要素に対してではなく新たに作成したDOM要素に対してレンダリングすることにより、画面に影響を与えることなくReactコンポーネントの処理を行えました。

この手法を使うことはあまりないかもしれませんがこんな技もあると思ってもらえれば🙋‍♂️

ちなみにですが新たに作成したDOM要素をブラウザ上に表示されるDOM要素の子要素として挿入(appendChild())してあげれば、簡単に画面に影響させられます👌

// index.jsx

import ReactDOM from 'react-dom/client'
import { App } from './App'

const tentativeElement = document.createElement('div')
document.getElementById('root').appendChild(tentativeElement) // 例えば、この行を追加する
const tentativeRoot = ReactDOM.createRoot(tentativeElement)
tentativeRoot.render(
  <App />
)

環境

実装や動作確認の際に使ったツールたちです。

OS macOS Monterey Version 12.5
Node.js v18.6.0
create-react-app v5.0.1

*対象のアプリはcreate-react-appを用いて作成しました。

関連資料