React 18 strict mode 에서 api 호출 두번 되는 문제 해결하기! (StrictMode 제거 없이)

Jinseok Lee·2022년 10월 9일
2

사연

react로 개인 프로젝트를 하는 와중에 api를 호출하는 부분이 들어갔는데, 컴포넌트가 두 번씩 렌더링 되면서 api도 두번씩 호출되는 상황이 발생하였다 ㅠㅠ (핵심적인 부분만 담기 위한 코드를 별도 작성하였다)

import { useEffect, useState } from "react"

export default function Todo() {
  const [todoList, setTodoList] = useState([])
  
  useEffect(() => {
    console.log('api 호출')
    setTodoList([
      {
        name: '설거지'
      }
    ])
  }, [])

  return (
    <div>
      <h2>Todo API 호출테스트</h2>
      {
        todoList.map((todo, todoIndex) => <p key={todoIndex}>{todo.name}</p>)
      }
    </div>
  )
}

전개

일단 google에 react 18 render twice를 검색했고 관련한 여러가지 블로그 글이나 스택오버 플로우 게시글들을 확인해보았다. 그러나 하나같이 <React.StrictMode>를 주석처리하는 얄팍한 방식을 쓰고 있었다 😭😭😭

해결

React 18에서 StrictMode가 사용되는 것은 컴포넌트가 빠르게 mount, unmount될때 문제가 발생할 수 있는 부분들 (주로 애니메이션 이벤트 등 DOM을 직접조작하는 이벤트)을 미리 검증하기 위하여 아주 빠르게 mount -> unmount -> mount 싸이클을 돌린다는 사실을 알게되었다.

그래서 mount가 완료가 되었다는 사실을 didMount라는 state에 저장하고 이를 토대로 마운트가 완료된 경우에 api를 호출 하는 방식으로 변경하였다.

import { useEffect, useState } from "react"
import { Link } from "react-router-dom"

let mountCount = 1

export default function Todo() {
  const [todoList, setTodoList] = useState([])
  const [didMount, setDidMount] = useState(false)

  useEffect(() => {
    console.log('mount: ', mountCount)
    mountCount++
    setDidMount(true)
    return () => {
      console.log('unmount')
    }
  }, [])

  useEffect(() => {
    console.log('didMount: ', didMount)
    if (didMount) {
      console.log('api 호출')
      const fetchData = [
        {
          name: '설거지'
        }
      ]
      setTodoList(fetchData)
    }
  }, [didMount])


  return (
    <div>
      <h2>Todo API 호출테스트</h2>
      {
        todoList.map((todo, todoIndex) => <p key={todoIndex}>{todo.name}</p>)
      }
      <Link to='/home'>Home</Link>
    </div>
  )
}

React.StrictMode로 인해 mount, unmout가 발생한 이후에 최종적으로 mount가 실행되는 것을 볼 수 있고 mount이후에 api 호출이 될 수 있도록 didMount값을 true로 변경시켜 주었다.

최초의 mount (mount: 1)시에 didMount값을 true로 변경하더라도 변경을 감지하지 업데이트 하지않고 최종적으로 mount (mount: 2) 될때만 state변경을 감지하여 useEffet(() => {}, [didMount])가 업데이트 되는 것을 볼 수 있었다.

참고

Solving the React 18 Double Render problem - https://www.youtube.com/watch?v=VUg7olsnusg

기타

api 호출의 경우 unmount 될때 취소하는 방식이 더 나은 방식으로 보여서 포스팅을 추가하였다.

React 18 strict mode에서 double mount로 인한 axios 중복 호출 막기 두번째 방법! (with AbortController)
https://velog.io/@sonaky47/React-18-strict-mode%EC%97%90%EC%84%9C-double-mount%EB%A1%9C-%EC%9D%B8%ED%95%9C-axios-%EC%A4%91%EB%B3%B5-%ED%98%B8%EC%B6%9C-%EB%A7%89%EA%B8%B0-%EB%91%90%EB%B2%88%EC%A7%B8-%EB%B0%A9%EB%B2%95-AbortController

profile
전 위메프, 이직준비중

0개의 댓글