개발을 하다보면 렌더링이 두번씩되어서 꼬이는 경우가 있다.
이유는 아래와 같이 여러가지가 있다.
javascript의 use strict와 비슷한 모드로 코드를 검사하는 역활을 한다. 검사항목으로는 아래와 같이 5가지 유형이 있다.
여기서 '예상치 못한 부작용 검사'가 두번 렌더링하기때문에 렌더링하는 것이다.
안전하지 않은 생명주기를 사용하는 컴포넌트 발견
componentWillMount 같은 지원 종료(deprecated)한 메서드를 사용하면 경고한다.
레거시 문자열 ref 사용에 대한 경고
(ref란? DOM에 직접 접근하는 기능)
현재는 객체 ref가 문자열 ref를 교체하는 용도가 되었기 때문에 문자열 ref의 사용에 대해 경고한다고 한다.
문자열 ref의 단점들
권장되지 않는 findDOMNode 사용에 대한 경고
컴포넌트 자식 중 특정 엘리먼트를 찾는 함수이다. 자식 엘리먼트는 구현 방식에 따라 달라질 수 있기 때문에 유지보수하기 어려워 사용을 자제한다. 대신 직접 ref를 지정해 사용하라고 권장한다.
예상치 못한 부작용 검사
리액트는 컴포넌트를 렌더링하기 위해 두 단계를 거친다. 렌더링과 커밋. 렌더링 단계에서는 변화를 계산하는 단계이다. 예를 들어 render 함수를 호출해서 이전값과 비교하는 방식이다. 이렇게 계산한 변경을 반영하는 단계가 커밋이다.
렌더링 단계에서 사용하는 메서드는 비교 계산을 위해 여러번 호출될 수 있는데 이 메서드 안에서 부작용을 포함하지 않는 것이 중요하다. 메모리 누수 등 예측하지 못한 문제를 일으킬수 있기 때문이다.
Strict 모드는 렌더링 단계의 메서드에 부작용이 있는지를 검사한다. 만약 리액트가 경고한다면 부작용 코드를 커밋 단계로 옮기는 것이 좋다. 렌더링 단계에서는 부작용 코드를 여러번 실행할 것이기 때문이다.
참고로 리액트는 Strict 모드에서 렌더링 단계의 메서드를 두 번씩 호출한다. 부작용 문제를 진단할 목적이다.
constructor, render, shouldComponentpdate, getDerivedStateFromProps
함수 컴포넌트 바디
State updater
useStaet, useMemo, useReducer
개발 모드에서만 적용됩니다. 생명주기 메서드들은 프로덕션 모드에서 이중으로 호출되지 않습니다.
레거시 context API 검사
레거시 context API는 오류가 발생하기 쉬워 이후 릴리즈에서 삭제될 예정입니다. 모든 16.x 버전에서 여전히 돌아가지만, Strict 모드에서는 아래와 같은 경고 메시지를 노출합니다.
https://reactjs.org/docs/strict-mode.html
// react의 경우
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.unregister();
// next.config.js
module.exports = {
reactStrictMode: false,
}
const tmap = useRef<any>(null)
useEffect(() => {
tmap.current = new Mmap(37.545555, 127.224, 16)
tmap.current.addPolygon(polygon)
}, [polygon])
const tmap = useRef<any>(null)
useEffect(() => {
tmap.current = new Map(37.545555, 127.224, 16)
tmap.current.addPolygon(polygon)
return () => {
tmap.current.destroy()
}
}, [polygon])
router에서 query를 취득해서 console.log로 내보내봤습니다.
const router = useRouter()
useEffect(() => {
const routeId = router.query.id
console.log(routeId)
}, [router])
// undefined가 처음 나오고 1이 나옵니다.
이것은 기분이 나쁩니다. 이를 해결하기위한 아래와 같이 여러 방법이 있습니다. 우선 추천은 3번째 방법입니다. 1번과 2번 방법은 query가 없은 경우를 상정하기가 어렵기 때문입니다.
const router = useRouter()
const [isFlag, setIsFlag] = useState(false)
useEffect(() => {
const routeId = router.query.id
if(!isFlag){
setIsFlag(true)
console.log(routeId)
}
}, [router])
// routeId
const router = useRouter()
useEffect(() => {
const routeId = router.query.id
if(routeId){
console.log(routeId)
}
}, [router])
// routeId
const router = useRouter()
useEffect(() => {
const routeId = router.query.id
if(router.isReady){
console.log(routeId)
}
}, [router])
//routeId
isReady: boolean - Whether the router fields are updated client-side and ready for use. Should only be used inside of useEffect methods and not for conditionally rendering on the server. See related docs for use case with automatically statically optimized pages
query의 취득 타이밍은 useEffect의 발화보다 나중이다는 것은 어쩐지 알아냈습니다만, 어떤 구조인지 궁금합니다.
Pre-rendering 동안 query는 빈 객체이며 hydration 프로세스가 끝난 단계에서 query 객체가 제공된다고 쓰고 있습니다.
getStaticProps나 getSererSideProps의 처리가 끝날 때까지는, query는 비어 있네요.
참조
https://sezzled.tistory.com/173
https://nextjs.org/docs/api-reference/next/router#router-object