TLDR:
1. React의 strict 모드를 이용하면 render가 두번 이상씩 될 수 있다.
2. 이는 개발환경에서만 발생한다.
첫 시작은 이게 아니었다....
처음의 의도와 기획은 render가 되는 것을 어떻게 count 할 수 있을까에 대한 호기심 이었다.
여정은 다음과 같다.
1. ref를 주고 받음에 치였고
2. forwardRef && typescript, useRef에 치였고
3. React.Strictmode에 치였다.
3개의 과정을 알아보고 인사이트를 찾아보자
오늘의 주제는 1번과 2번이 아니다.
1번과 2번은 빠르게 훑고 갈것이다.
1번에서는 ref를 부모에서 주고 자식에서 카운팅을 하나씩 올리려 했었다.
2번에서는 forwardRef를 통해 ref를 이름의 변경없이 보내서 자식에서 카운팅을 하려 했었다.
간단하게만 말하자면
1번은 개념적으로 틀렸었고
2번은 typescript의 설정에서 헤매었다. forwardRef를 사용하면 ref는 자식컴포넌트의 두번재 인자로 들어간다
(props, ref) => 요런식,
forwardRef의 낮은 이해와 타입스크립트 설정에서 실패했었고
2번에 관하여서는 다음에 포스팅 할거다!!
이제 3번이다.
구현 영상부터 한번 보자
**나는 strict mode에서 2번씩 카운트가 올라가는게 궁금했다.**
그래서 불나방같이 달려들었다.
카운팅 해주는 방식은 아래의 코드와 같다.
놀랄만큼 별거 없다.
useRef를 선언해주었고 current의 값을 렌더링 될때마다 올려줄 수 있도록 하였다.
import React, { useState } from "react";
const Counter = () => {
const [ count, setCount ] = useState<number>(0);
const testRef = React.useRef(0);
testRef.current++
let vv = { current: 0 };
console.log(vv.current++);
const handleIncrementClick = () => {
setCount(count => count + 1);
}
return (
<div style={{ alignSelf: "center" }}>
<p>count = {count}</p>
<p>renderCount = {testRef.current}</p>
<button onClick={handleIncrementClick}> Increment </button>
</div>
);
};
export default Counter;
testRef.current 아래를 보면
let vv로 시작하는 두줄짜리 코드가 있다.
이건 useRef의 동작을 좀 더 확인해보고 싶어서 추가 하였다.
저 코드의 동작 결과인 콘솔에는 어떤 값이 찍힐까 ????? // 0이 찍히는건 안 함정
결론을 말하자면 useRef는 매번 렌더링을 할 때 동일한 ref 객체를 제공해 준다.
useRef()는 순수 자바스크립트 객체를 생성해 준다. 근데 {current:}를 생성하는거랑 뭔 차이가 있을까 싶었다.
useRef는 렌더링 할때 동일한 객체를 계속 리턴해준다. 그래서 렌더링 할때마다 count++가 원하는대로 동작할 수 있었다.
싱글인스턴스를 한번만 생성하고 해당 인스턴스를 매번 가져와 사용하는것과 같다고 느껴졌다.
자 이제 렌더링 하는 코드는 되었고 이제야 본론인 Strict Mode 이다
결론부터 말하자면 (이 말을 자주쓰네 ㅎㅎ) Strict Mode가 검사하려고 렌더를 두번씩 시키는 거다.
상황에 따라 두번 이상씩 될수도 있다.
StrictMode가 뭐지 하는 궁금중이 들수도 있다.
strict의 뜻은 엄격하다 라는 뜻이다.
strict + mode 는 엄격모드 이다.
javascript에서는 'use strict'를 추가해서 사용할 수 있고
typescript에서는 tsconfig.json에서 strict 관련 옵션을 주어서 설정할 수도 있다.
이것의 기능은 검열을 더 빡세게 해서 에러를 검출해 내겠다 요런 느낌으로 이해하였다.좀더 상세한 설명은 아래의 링크에서 확인이 가능하다
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Strict_mode
이런게 react에도 있다.
<React.StrictMode></React.StrictMode> 로 감싸면 안에 chidren들은 엄격모드가 적용된다.
React에서는 다음과 같은 기능들을 수행한다.
1. 안전하지 않은 생명주기를 사용하는 컴포넌트 발견
2. 레거시 문자열 ref 사용에 대한 경고
3. 권장되지 않는 findDOMNode 사용에 대한 경고
4. 예상치 못한 부작용 검사
5. 레거시 context API 검사
여기의 4번이 렌더링을 두번씩 시키고 있었다(찾았다 범인!!)
렌더링 두번씩 시키는것을 이해하려면 약간의 설명이 필요하다
React는 두 단계로 동작한다고 보면 된다.
렌더링 -> 커밋
렌더링 : 어떤 변화가 필요한 지 결정하는 단계
내가 이해한 내용은 react에서 render를 호출하고 이를 기존의 가상돔과 비교하여 변경이 필요한 곳을 결정하는 것으로 이해하였다.
커밋 : 렌더링에서 결정된 변화가 필요한 곳에 실제로 반영을 하는 과정
React는 커밋하기 전에 렌더링을 여러번 호출하거나 커밋을 아예 하지 않을 수(에러 발생, 우선순위에 따른 작업 중단)도 있다고 한다
렌더링 에서 생명주기를 이루는 메서드들은 아래의 함수들과 같다
- constructor
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- setState 업데이트 함수
위의 함수들은 렌더링을 유발한다 그래서 이 함수들 내부에는 부작용이 없는것이 중요하다!!
Strict 모드에서는 자동으로 부작용을 찾아낼 수는 없다. 그래서 아래의 함수를 의도적으로 이중으로 실행시켜 혹시 모를 렌더링에서의 부작용을 체크한다고 한다
- 클래스 컴포넌트의 constructor, render, shouldComponentUpdate
- getDerivedStateFromProps static 메서드
- 함수 컴포넌트 바디
- State updater 함수 (setState의 첫번째 인자)
- useState, useMemo, useReducer에 전달되는 함수
React에서의 예시를 그대로 가지고 와 보겠다.
class TopLevelRoute extends React.Component {
constructor(props) {
super(props)
ShareApplicationState.recordEvent("ExampleComponent");
}
}
만약에 sharedApplicationState.recordEvent 함수의 결과가 계속 달라진다면?? => 컴포넌트가 여러번 인스턴스화 되었을때 어플리케이션의 상태가 잘못된 방향으로 갈수도 있다.
이와 같이 일관성이 없는 버그들을 strict mode에서는 찾기를 원했고 선택한 방법이 constructor를 의도적으로 두번 호출한 것이다.
아래는 render 테스트 코드인데
inline style이 지저분하게 들어가있는건 착시현상이다.
감안하고 넘어가자
import React, { useRef, useState } from 'react';
import Counter from './Counter';
function App() {
const [count, setCount] = useState(0);
const testRef = useRef(0);
testRef.current++
return (
<div
style={{
padding: "50px",
display: "flex",
flexDirection: "column",
justifyContent: "end",
}}
>
<div style={{ alignSelf: "center" }}>
<div>count = {count}</div>
<div>renderCount = {testRef.current}</div>
<button
onClick={() => setCount((prev) => prev + 1)}
style={{ display: "inline" }}
>
클릭
</button>
</div>
<h3 style={{ alignSelf: "center", marginTop: "3rem" }}>안 Strict Mode</h3>
<Counter />
<React.StrictMode>
<h3 style={{ alignSelf: "center" }}>Strict Mode</h3>
<Counter />
</React.StrictMode>
</div>
);
}
export default App;
https://ko.reactjs.org/docs/hooks-reference.html#useref
https://ko.reactjs.org/docs/strict-mode.html
'Frontend > React' 카테고리의 다른 글
React useLayoutEffect (0) | 2022.03.03 |
---|---|
제어 컴포넌트 vs 비제어 컴포넌트 (0) | 2022.03.03 |
cra + typescript + jest (0) | 2022.02.23 |
넘블챌린지 [Square Select Game] (0) | 2022.02.10 |
cannot get /path (0) | 2022.02.07 |