프로젝트 최적화

조뮁·2023년 1월 23일

React

목록 보기
31/34

코드 최적화 확인 방법
1. 코드를 보고 파악하기 (정적 분석)
2. 프로그램을 활용하여 파악하기 (동적 분석)


날짜 변경 시 필터영역 랜더링 방지

원인

날짜 변경 버튼을 누르면 setState(상태변화함수)가 실행되고, state가 변경되어 Home 컴퍼넌트가 리랜더링됨

  • DiaryList는 Home 컴퍼넌트의 자식요소이기 때문에, Home 컴퍼넌트가 랜더되면 DiaryList도 랜더됨.
  • DiaryList의 자식인 ControlMenu 컴퍼넌트도 랜더됨.

해결

React.memo

  • React.memo()의 인자로 컴포넌트를 전달하면, 강화된 컴포넌트를 돌려주는 고착 컴포넌트임
    • 고착 컴포넌트 : 전달받는 prop의 값이 바뀌지 않으면 랜더링이 일어나지 않음
const ControlMenu = React.memo(({ value, onChange, optionList }) => {
  return (
    <select
      className="ControlMenu"
      value={value}
      onChange={(e) => onChange(e.target.value)}
    >
      {optionList.map((it, idx) => (
        <option key={idx} value={it.value}>
          {it.name}
        </option>
      ))}
    </select>
  );
});

...

return (
    <div className="DiaryList">
      <div className="menu_wrapper">
        <div className="left_col">
          <ControlMenu
            value={sortType}
            onChange={setSortType}
            optionList={sortOptionList}
          />
          <ControlMenu
            value={filter}
            onChange={setFilter}
            optionList={filterOptionList}
          />
        </div>
        <div className="right_col">
          <MyButton
            type={"positive"}
            text={"새 일기쓰기"}
            onClick={() => navigate("/new")}
          ></MyButton>
        </div>
      </div>
...
)
  • onChange함수는 useCallback등으로 함수를 재사용하게 만들지 않으면, 부모 컴포넌트가 리랜더링 될 때 같이 변경돼서 React.memo 등의 기능이 정상적으로 동작하지 않음. 하지만, ControlMenu에 전달되는 onChange함수가 별도의 useCallback처리를 하지 않았음에도 리랜더링이 발생하지 않음
  • useState에서 반환받은 상태변화함수(setState 함수)는 랜더링이 발생해도 동일한 id를 보장함 (=기본적으로 useCallback 처리가 되어있음)
    --> 별도의 useCallback 처리를 한 함수를 만드는 대신, useState의 상태변화함수를 내려주면 더 쉽게 컴포넌트 최적화를 할 수 있음.

필터 선택 시 필요없는 일기아이템 리랜더링 방지

원인

필터값이 변경되면 DiaryList의 state가 변경되기 때문에, 자식 컴퍼넌트인 DiaryItem 컴퍼넌트로 리랜더링됨

// DiaryList.js
const DiaryList = ({ diaryList }) => {
  const navigate = useNavigate();
  const [sortType, setSortType] = useState("lastest");
  const [filter, setFilter] = useState("all");
  ...

해결

import { useNavigate } from "react-router-dom";
import React, { useContext } from "react";
import { DiaryDispatchContext } from "./../App.js";
import MyButton from "./MyButton";

...

export default React.memo(DiaryItem);

일기 수정 시 emotion item 리랜더링 방지

원인

일기 내용 변경 시, DiaryEditor.js의 content state가 변경됨 -> 자식 컴퍼넌트인 EmotionItem도 리랜더링됨

// EmotionItem.js
import React from "react";
...
export default React.memo(emotionItem);

해결

위와같이 React.memo로 묶어주었음에도 계속 리랜더링됨.

  • 전달받은 prop 중 onClickFunc이 문제
    • useState를 통해 전달받은 상태변화함수가 아니거나, useCallback으로 묶은 함수가 아니기때문
      -> Diaryeditor 컴퍼넌트에서 onClickFunc을 useCallback으로 재사용할 수 있도록 변경
// DiaryEditor.js
import { useState, useRef, useContext, useEffect, useCallback } from "react";

  // 감정상태 변화 함수 따로 빼기
  const handleClickEmote = useCallback((slctEmotionId) => {
    setslctEmotionId(slctEmotionId);
  }, []);


return (
<section>
  <h4>오늘의 감정</h4>
  <div className="input_box emotion_list_wrapper">
  {emotionList.map((it) => (
  <EmotionItem
  key={it.emotion_id}
{...it}
// callBack안에서 또 useCallback을 사용할 수 없어서 함수를 따로 생성해서 빼내기
onClickFunc={handleClickEmote}
isSelected={it.emotion_id === slctEmotionId}
/>
  ))}
    </div>
</section>
)

0개의 댓글