Reactのレンダリング順について

Reactのレンダリング順について

useEffect系はコンポネント順ではない

考えてみると当たり前だが、順番的には子コンポーネントのレンダリング準備が整ってから親コンポーネントのレンダリングに取り掛かる

なので、親のuseEffectやuseLayoutEffectは子コンポーネントのそれらが終えてから実行される

import React, {useState, useLayoutEffect, createContext, useContext} from "https://cdn.skypack.dev/[email protected]";
import * as ReactDOM from "https://cdn.skypack.dev/[email protected]";

const StateContext = createContext({})
const App = () => {
  const [state, setState] = useState([])
  useLayoutEffect(() => {
    console.log("parent layout effect")
    setState((currentState) => [...currentState, "parent"])
  }, [])
  
  const stateContextProps = {
    state: state,
    setState: setState
  }
  console.log("parent")
  console.log(state)
  return (
    <StateContext.Provider value={stateContextProps}>
      <ChildComponent name="child1"/>
      <ChildComponent name="child2"/>
      <ChildComponent name="child3"/>
    </StateContext.Provider>
  )
}

const ChildComponent = ({name}) => {
  const stateContext = useContext(StateContext)
  useLayoutEffect(() => {
    console.log(name + " layout effect")
    stateContext.setState((currentState) => [...currentState, name])
  }, [])

  console.log(name)

  return (
    <div> {name} </div>
  )
}

ReactDOM.render(<App />, document.querySelector('#app'))

↓ 実行ログ

"parent"

// [object Array] (0)
[]

"child1"

"child2"

"child3"

"child1 layout effect"

"child2 layout effect"

"child3 layout effect"

"parent layout effect"

"parent"

// [object Array] (4)
["child1","child2","child3","parent"]

"child1"

"child2"

"child3"

実行順は親→子コンポーネント(親のreturn内で初めて子コンポーネントが呼ばれるので)
にもかかわらず、useLayoutEffectの順は子→親。

親は子の最後にuseEffectされる点に注意

useContextでsetStateを渡して子コンポーネント内で親のstateを操作するような場合、親の操作するタイミングには注意する必要があるようです。

例えば親→子の順にstateに値を追加していきたい場合は、親が最後に呼ばれるので、下記のように親を先頭に追加するよう記載する必要があります。

  useLayoutEffect(() => {
    console.log("parent layout effect")
    setState((currentState) => ["parent", ...currentState])
  }, [])

↓ 実行ログ

// [object Array] (4)
["parent","child1","child2","child3"]