React : Rendering vs Re-Rendering & React.memo + useCallback
[ React 에서 페이지가 렌더링 되는 시점 ]
① 최초 페이지가 처음 열릴때 , JSX 안의 상태(변수) , 함수 그리고 컴포넌트 등이 처음 Rendering 이 된다 ② 상태(변수)가 1개라도 변경된다면, 현재 전체 페이지의 함수 및 변수등이 모두 Re-Rendering 된다 (useStae , useReducer 로 설정된 여러개의 변수가 있을때, 그중 1개라도 값이 변경될 경우 해당) ③ 부모 컴포넌트에서 Re-Rendering 이 일어난다면 자식 컴포넌트들도 모두 다시 Re-Rendring 된다 ④ props 가 변경되면(함수재성성 또는 값) 모두 다시 Re-Rendering 한다. (props로 전달되는 변수들 또는 함수가 변경 될 경우(함수재생성 – 함수안의 변수의 변경으로 함수가 다시 만들어질경우) ⑤ forceUpate 함수가 실행될때 Re-Rendering 이 일어난다 (클래스 컴포넌트방식으로 코딩 할 경우 해당됨) |
[ Re-Rendering 범위 ]
App 안에 모든 코드들이 렌더링 & 리렌더링됨 |
< App.js > import React from ‘react’ import {useState} from ‘react’ import SubComp1 from ‘./components/subComponent1’; import SubComp2 from ‘./components/subComponent2’; import SubComp3 from ‘./components/subComponent3’; function App() { console.log(“★ App render…………………………”) const [val1 , setVal1] = useState(”); const [val2 , setVal2] = useState(”) const [val3 , setVal3] = useState(”) const onChangeVal1 = (e) => { setVal1(e.target.value) } const onChangeVal2 = (e) => { setVal2(e.target.value) } const onChangeVal3 = (e) => { setVal3(e.target.value) } return( <div> <SubComp1 value={val1} onChangeEvent={onChangeVal1}></SubComp1> <SubComp2 value={val2} onChangeEvent={onChangeVal2}></SubComp2> <SubComp3 value={val3} onChangeEvent={onChangeVal3}></SubComp3> </div> ) } export default App; |
[ SubComponent1.js ] import React from ‘react’ function SubComponent1(props) { console.log(“★ Render SubComponent 1 …………”) return <div>SubComponent1 [val1] : <input type=”text” onChange={props.onChangeEvent}></input> {props.value}</div> } export default SubComponent1;
[ SubComponent2.js ] import React from ‘react’ function SubComponent1({value , onChangeEvent}) { console.log(“★ Render SubComponent 2 …………”) return <div>SubComponent2 [val2] : <input type=”text” onChange={onChangeEvent}></input> {value}</div> } export default SubComponent3; [ SubComponent3.js ] import React from ‘react’ function SubComponent1({value , onChangeEvent}) { console.log(“★ Render SubComponent 3 …………”) return <div>SubComponent3 [val3] : <input type=”text” onChange={onChangeEvent}></input> {value}</div> } export default SubComponent3; |
초기 렌더링 – 최초 페이지 로딩하면서 전체 렌더링 됨 |
console.log(“★ App render…………………………”) // 최초 렌더링 console.log(“★ Render SubComponent 1 …………”) // 최초 렌더링 console.log(“★ Render SubComponent 2 …………”) // 최초 렌더링 console.log(“★ Render SubComponent 3 …………”) // 최초 렌더링 페이지가 첫 로딩되면서 모두 렌더링되었다.. 이제 아래 예시를 통해 상태변수를 변경하여 리-렌더링 일으켜보자 * 리렌더링 |
* <Subcomponent1 컴포넌트의 onChangeEvent 이벤트를 발생해서 setVal1(e.target.value) 상태 변경 (상태(변수)가 1개라도 변경된다면, 현재 페이지의 함수 및 상태(관련없는 다른상태 모두 포함)등이 Re-Rendering 된다) | |
const onChangeVal1 = (e) => { setVal1(e.target.value) } <SubComp1 value={val1} onChangeEvent={onChangeVal1}></SubComp1> [출력로그] console.log(“★ App render…………………………”) // 렌더링 발생 – 정상 console.log(“★ Render SubComponent 1 …………”) // 렌더링 발생 – 정상 console.log(“★ Render SubComponent 2 …………”) // 렌더링 발생 – 불필요함 console.log(“★ Render SubComponent 3 …………”) // 렌더링 발생 – 불필요함 [ React.memo ] 컴포넌트 memoization 하기 —————————————————————————————————————————————————————— React.memo 함수는 컴포넌트를 memoizaion 하여 부모에서 호출시 props 가 변경되지 않으면 새로 렌더링 하지 않는다. ( ★★★ props 가 변경되는 사항은 props 로 전달되는 상태 변수의 값의 변경 또는 함수의 재생성 등) → SubComponent1.js ,SubComponent2.js , SubComponent3.js 컴포넌트 export 시 함수 React.memo 적용export default React.memo(SubComponent1); export default React.memo(SubComponent2); export default React.memo(SubComponent3); React.memo 함수로 memoization 했으니, 다시 테스트 ,,,, (SubComponent 1 만 리렌더링 되어야 원하는 결과이다.) [출력로그] console.log(“★ App render…………………………”) // 렌더링 발생 – 정상 console.log(“★ Render SubComponent 1 …………”) // 렌더링 발생 – 정상 console.log(“★ Render SubComponent 2 …………”) // 렌더링 발생 – 불필요함 console.log(“★ Render SubComponent 3 …………”) // 렌더링 발생 – 불필요함
SubComp1 을 통해 setVal1(e.target.value) 를 하여 Subcomponent1이 사용하는 val1 이 변경되었으니 console.log(“★ App render…………………………”) // 렌더링 발생 – 정상 하지만 , setVal1(e.target.value) 와 관계없는,
결과적으로 onChangeVal1 함수를 통해 setVal1 이 val1 상태를 변경하였기 때문에 , 아래 자식 컴포넌트의 변경된 props를 살펴보면 , <SubComp1 value={val1} onChangeEvent={onChangeVal1}></SubComp1> // val1 변경 O , onChangeVal1 변경 O <SubComp2 value={val2} onChangeEvent={onChangeVal2}></SubComp2> // val2 변경 X , onChangeVal2 변경 O <SubComp3 value={val3} onChangeEvent={onChangeVal3}></SubComp3> // val2 변경 X , onChangeVal3 변경 O 결국 리렌더링 조건의 ②,③,④ 조건에 모두 만족하여 리-렌더링이 발생하였다.
|
[ useCallback ] – 함수 memoization 하기 | |||||
App() 컴포넌트 안에서 상태가 변경되더라도 , ★ Render SubComponent 2 …….. , ★ Render SubComponent 3 와 관련없는 상태라면 리-렌더링 되지 않게 하려면, useCallback 을 이용하여 함수자체를 memoization 해야함
함수 전체를 useCallback 으로 감싸고 , [의존성배열] 에 이 함수가 리-렌더링시 새롭게 생성 하기위해 의존할 상태변수들을 나열해주면, 나열한 상태 변수들 중 한 개 라도 값이 변하면 , 리렌더링시 함수를 재생성 하게된다. ※ 의존성 배열을, 빈 배열로 [ ] 설정 할 경우, 최초 로딩시 렌더링되고 이후로는 절대 리-렌더링 되지 않는다. 의존성 배열을 지정 해야 하는 이유는, 함수가 메모리에 저장 될 때 상태 값까지 그대로 저장되므로,
|
재생성되는 함수의 리렌더링을 막기위해서는 useCallback 을 통해 함수를 memoization 한다. const onChangeVal1 = useCallback ( (e) => { setVal1(e.target.value) } , [ ] ) const onChangeVal2 = useCallback ( (e) => { setVal2(e.target.value) } , [ ] ) const onChangeVal3 = useCallback ( (e) => { setVal3(e.target.value) } , [ ] )의존성 배열에 [ ] 빈 배열로 설정하면 최초 렌더링시만 함수를 생성하며, 그뒤로는 리-렌더링 하지않음
<SubComp1 value={val1} onChangeEvent={onChangeVal1}></SubComp1> // val1 변경 O , onChangeVal1 변경 X <SubComp2 value={val2} onChangeEvent={onChangeVal2}></SubComp2> // val2 변경 X , onChangeVal2 변경 X <SubComp3 value={val3} onChangeEvent={onChangeVal3}></SubComp3> // val2 변경 X , onChangeVal3 변경 X
★ 참고로 아래 예제처럼 함수를 memoization 할때, 의존성 배열에 나열된 상태변수가, 함수안에서 사용 될때만 나열하고 const myFunction = useCallback( () => { |