혼자 적어보는 노트

[React] 부모 state변경 시 자식 컴포넌트 rerender 본문

React

[React] 부모 state변경 시 자식 컴포넌트 rerender

jinist 2021. 12. 21. 05:13

리액트에서 부모 컴포넌트에 state를 만들고 해당 state값을 변경하는 이벤트를 실행 했을 때
해당 state를 사용하는 자식 컴포넌트가 아닌 다른 자식 컴포넌트들 전부 render함수가 호출되는 것을 볼 수 있다.


state변경시 VDOM의 스마트한 특성으로 elements창에서는 해당하는 요소들만 변경이 되지만
각 부모, 자식1, 자식2 컴포넌트에 console.log함수를 넣어놓는다면 이상하게도
자식 1번의 컴포넌트에서 props를 통한 state변경을 한다면
해당 state에 관여하지 않는 자식 2번의 컴포넌트의 re-render되어 console.log가 찍히는 것을 확인할 수 있다.


리렌더링을 막을 수 있는 방법엔 어떤 것들이 있을까?


class 컴포넌트에서의 PureComponent


클래스 컴포넌트의 Component로 설정한 부분을 PureComponent 로 변경한다면
rerender를 막을 수 있다.

pureComponent는 전달받은 props와 state를 비교하여 이전의 값과 "얕은"비교를 하여
변경된 사항이 없다면 rerender를 하지 않는다.

얕은 비교를 하기 때문에 state안의 깊은 depth의 데이터가 변경된다면
pureComponent는 이부분을 인지 하지 못하고 update를 해야될 상황에서
무시를 해버리는 경우가 발생할 수 있다.

아래는 버튼 클릭 시 user의 gender를 변경하는 간단한 코드를 작성해보았다.

 

부모 : userProfiles.js / 자식 : UserProfile, TestComponent

// 부모 컴포넌트 UserProfiles.js

import React, { Component } from "react";
import UserProfile from "./UserProfile";
import TestComponent from "./TestComponent";

class UserProfiles extends Component {
  state = {
    users: [
      {
        id: 1,
        name: "Jay",
        age: 28,
        gender: "Female",
        favorites: ["movie", "food"],
      },
      {
        id: 2,
        name: "May",
        age: 23,
        gender: "Male",
        favorites: ["movie", "car", "beauty"],
      },
    ],
  };
  handleGenderChange = (user) => {
    const users = this.state.users.map((item) => {
      if (item.id === user.id) {
        return { ...user, gender: user.gender === "Male" ? "Female" : "Male" };
      } else {
        return item;
      }
    });
    this.setState({ users });
  };
  render() {
    return (
      <div>
        <ul>
          {this.state.users.map((user) => (
            <UserProfile
              key={user.id}
              user={user}
              onGenderChange={this.handleGenderChange}
            />
          ))}
        </ul>
        <TestComponent /> // ** Test component
      </div>
    );
  }
}

export default UserProfiles;
// 자식컴포넌트 UserProfile.js

import React, { PureComponent } from "react";

class UserProfile extends PureComponent {
  handleGenderChange = () => {
    this.props.onGenderChange(this.props.user);
  };
  render() {
    const { name, age, gender } = this.props.user;
    console.log(name, "UserProfile");
    return (
      <div>
        <li>
          <p>Name : {name}</p>
          <p>Age : {age}</p>
          <p>
            Gender : {gender}
            <button onClick={this.handleGenderChange}>Change</button>
          </p>
        </li>
      </div>
    );
  }
}

export default UserProfile;

gender변경 시 자식 컴포넌트 중 state변경에 해당하는 컴포넌트만 변경이 된다.

 

(React dev 툴의 설정의 Highlight updates when components render. 설정 시 좀 더 쉽게 확인 할 수 있다.)

 

 

 

Function 컴포넌트에서의 React.memo


React.memo는 클래스 컴포넌트에서의 PureComponent처럼 rerender을 막아주는 컴포넌트이다.
컴포넌트가 같은 props를 받고 같은 결과를 렌더링하게된다면 React.memo를 호출하여
메모이징 한다.
컴포넌트를 렌더링 하지 않아도 마지막의 렌더링 결과를 재사용하게 되는데
즉 prevProps와 nextProps를 비교하여 같은값을 가진다면 이전의 렌더링 결과를 재사용 하는 것이다.

React.memo는 props의 변화에만 영향을 주기때문에 해당 컴포넌트 내부에서
useState, UseReducer, UseContext를 사용해서 state나 context를 변화시킬 때는
정상적으로 rerendering이 된다.

 

아래는 function 컴포넌트에서의 React.memo코드이다.

// 자식컴포넌트 UserProfile.js

import React from "react";

const UserProfile = React.memo((props) => {
  const { name, age, gender } = props.user;
  const handleGenderChange = () => {
    props.onGenderChange(props.user);
  };
  console.log(name, "UserProfile");
  return (
    <div>
      <li>
        <p>Name : {name}</p>
        <p>Age : {age}</p>
        <p>
          Gender : {gender}
          <button onClick={handleGenderChange}>Change</button>
        </p>
      </li>
    </div>
  );
});

export default UserProfile;

 

 

-

알게된 점.

 

리액트의 부모에 있는 state변경 시 자식 컴포넌트들이 렌더링이 다시 되는 것은 알고 있었으나,

이것을 컨트롤 할 수 있다는 것을 알게되었다.

PureComponent나 React.memo나 두 개 모두 데이터를 "얕은비교"를 하기 때문에

state값을 변경할 때 불변성을 지키며 새로운 값으로 할당을 하게 되는 부분 또한

이 렌더링 과정에 영향을 준다는 것을 알게 되었다.

이전부터 느낀 것이지만 deep depth의 객체 값을 다루는건 조금 까다롭다.

라이브러리를 사용하여 다룰수도 있겠지만 이번 예시 외에도

이전에 객체의 얕은 복사로 인한 문제 사항들을 직접 몇차례 겪어봤기 때문에

이해가 좀 더 잘 되었던 것 같다.

가볍게 사용법을 알아보려고 했던 것이였지만 많은 것을 얻게되었고 많은 것이 또 궁금해졌다.

Comments