좌충우돌 개발자의 길

[토이프로젝트] 리렌더링 왜 안되는거야? 본문

PROJECTS/Simple Wallet : 심플한 가계부

[토이프로젝트] 리렌더링 왜 안되는거야?

sustronaut 2022. 11. 16. 15:45

# 트러블 슈팅

초기 데이터로 bar chart가 잘 그려지는데 나중에 데이터가 추가적으로 입력되면 자동으로 차트에 반영되지 않는 현상이 발생했다.

 

# 해결 과정

## 원인분석

// 기존 코드
const dataset = useRecoilValue(datasetState);
const filterDataset = dataset.filter(
      (value) => Number(value.year) === today.getFullYear()
    );
    
    // monthData에 값 더하기
    filterDataset.map((value) => {
      const month = Number(value.month)-1;
      if (value.accountType === "Deposit") {
        monthData[month].totalDeposit += Number(value.price);
      } else {
        monthData[month].totalWithdraw += Number(value.price);
      }
    }
);

1. 데이터 입력 즉시, 그래프가 반영되지 않는다? => 리렌더링이 되지 않았다!

=> useEffect를 사용해 리렌더링을 해보자!

 

## useEffect로 리렌더링 해보기

 // 기존 데이터
 const dataset = useRecoilValue(datasetState);
 
 // 차트에 입력할 데이터
  const monthData = [
    { month: "1", totalDeposit: 0, totalWithdraw: 0 },
    { month: "2", totalDeposit: 0, totalWithdraw: 0 },
    { month: "3", totalDeposit: 0, totalWithdraw: 0 },
    { month: "4", totalDeposit: 0, totalWithdraw: 0 },
    { month: "5", totalDeposit: 0, totalWithdraw: 0 },
    { month: "6", totalDeposit: 0, totalWithdraw: 0 },
    { month: "7", totalDeposit: 0, totalWithdraw: 0 },
    { month: "8", totalDeposit: 0, totalWithdraw: 0 },
    { month: "9", totalDeposit: 0, totalWithdraw: 0 },
    { month: "10", totalDeposit: 0, totalWithdraw: 0 },
    { month: "11", totalDeposit: 0, totalWithdraw: 0 },
    { month: "12", totalDeposit: 0, totalWithdraw: 0 },
  ];
 
 useEffect(() => {
    // 올해 연도의 데이터만 가져오기
    const filterDataset = dataset.filter(
      (value) => Number(value.year) === today.getFullYear()
    );
    
    // monthData에 값 더하기
    filterDataset.map((value) => {
      const month = Number(value.month)-1;
      if (value.accountType === "Deposit") {
        monthData[month].totalDeposit += Number(value.price);
      } else {
        monthData[month].totalWithdraw += Number(value.price);
      }
    });
  }, [monthData]);

- 먼저, useEffect를 사용해서 monthData가 바뀔 때만 실행되도록 설정하였다.

- 하지만 여전히 되지 않았다... useEffect로만 사용하면 되지 않을 것 같아 비슷한 기능을 살펴봤다

- CreateAccountHistory.js 에서 입출금 거래 내역이 즉시 반영되었을 때는 useState함수를 써서 데이터값이 바뀌면 리렌더링 되게 만들었다.

=> useState를 사용해 리렌더링을 해보자!

 

## useState로 리렌더링 해보기

 // 기존 데이터
 const dataset = useRecoilValue(datasetState);
 
 // 차트에 입력할 데이터
  const [monthData, setMonthData] = useState([
    { month: "1", totalDeposit: 0, totalWithdraw: 0 },
    { month: "2", totalDeposit: 0, totalWithdraw: 0 },
    { month: "3", totalDeposit: 0, totalWithdraw: 0 },
    { month: "4", totalDeposit: 0, totalWithdraw: 0 },
    { month: "5", totalDeposit: 0, totalWithdraw: 0 },
    { month: "6", totalDeposit: 0, totalWithdraw: 0 },
    { month: "7", totalDeposit: 0, totalWithdraw: 0 },
    { month: "8", totalDeposit: 0, totalWithdraw: 0 },
    { month: "9", totalDeposit: 0, totalWithdraw: 0 },
    { month: "10", totalDeposit: 0, totalWithdraw: 0 },
    { month: "11", totalDeposit: 0, totalWithdraw: 0 },
    { month: "12", totalDeposit: 0, totalWithdraw: 0 },
  ]);
 
 useEffect(() => {
    // 올해 연도의 데이터만 가져오기
    const filterDataset = dataset.filter(
      (value) => Number(value.year) === today.getFullYear()
    );
    
    // monthData에 값 더하기
    filterDataset.map((value) => {
      const month = Number(value.month)-1;
      if (value.accountType === "Deposit") {
	      const findIndex = monthData.findIndex(element => element.code == month);
		  let copyArray = [...monthData];
          if(findIndex != -1) {
          	const totalDeposit = month[findIndex].totalDeposit + Number(value.price);
        	copyArray[findIndex] = {...copyArray[findIndex], totalDeposit: totalDeposit};
          }
      } else {
        // if문 코드와 비슷
        ...
      }
    });
  }, [monthData]);

- monthData를 useState를 적용해서 구현했다.

- 하지만.... 이 역시 안됐다. 분명 잘 monthData값에 새로운 데이터로 들어갔는데 리렌더링이 되지 않았다.

- 난 이쯤에서 리렌더링이 되는 조건에 대해서 다시 조사를 시작했다.

 

## 리렌더링 되는 조건

  1. 컴포넌트 자신의 state가 변하기
  2. 부모 컴포넌트로부터 받는 props가 변하기
  3. 부모 컴포넌트가 리렌더링 되기

출처 : https://velog.io/@sham/%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81%EA%B3%BC-%EB%A7%88%EC%9A%B4%ED%8A%B8-useEffect

 

- 난 useState만 리렌더링하게 만들 수 있다고 생각했는데 아니었다!! 그래서 가장 적합한 2번으로 리렌더링을 시도해봤다.

 

## 부모 컴포넌트로부터 받는 props가 변하기

// Main.js
 
const dataset = useRecoilValue(datasetState);
return (
    <Wrapper>
      <Title />
      <Alarm />
      <Total />
      <GraphAccount dataset={dataset} /> // dataset을 부모 컴포넌트에서 props로 전달해주기
      ...
    </Wrapper>
  );
  
  // GraphAccount.js 
  function GraphAccount({dataset}) {
  const today = new Date(); // 오늘 날짜

  // 차트에 입력할 데이터
  const monthData = [
    { month: "1", totalDeposit: 0, totalWithdraw: 0 },
    { month: "2", totalDeposit: 0, totalWithdraw: 0 },
    { month: "3", totalDeposit: 0, totalWithdraw: 0 },
    { month: "4", totalDeposit: 0, totalWithdraw: 0 },
    { month: "5", totalDeposit: 0, totalWithdraw: 0 },
    { month: "6", totalDeposit: 0, totalWithdraw: 0 },
    { month: "7", totalDeposit: 0, totalWithdraw: 0 },
    { month: "8", totalDeposit: 0, totalWithdraw: 0 },
    { month: "9", totalDeposit: 0, totalWithdraw: 0 },
    { month: "10", totalDeposit: 0, totalWithdraw: 0 },
    { month: "11", totalDeposit: 0, totalWithdraw: 0 },
    { month: "12", totalDeposit: 0, totalWithdraw: 0 },
  ];


  useEffect(() => {
    // 올해 연도의 데이터만 가져오기
    const filterDataset = dataset.filter(
      (value) => Number(value.year) === today.getFullYear()
    );

    // monthData에 값 더하기
    filterDataset.map((value) => {
      const month = Number(value.month) - 1;
      if (value.accountType === "Deposit") {
        monthData[month].totalDeposit += Number(value.price);
      } else {
        monthData[month].totalWithdraw += Number(value.price);
      }
    });
  }, [monthData]);

- 성공했다! 리렌더링이 제대로 이루어지지 않아 차트에 바로 입력된 값이 반영되지 않았던 것이다.