프론트엔드 첫걸음

함수형 컴포넌트를 클래스 컴포넌트로 바꾸기 2 - useEffect를 componentDidUpdate로 본문

개발 공부/React

함수형 컴포넌트를 클래스 컴포넌트로 바꾸기 2 - useEffect를 componentDidUpdate로

차정 2022. 8. 5. 12:25
[Udemy] React Complete guide section 13.

 

함수형 컴포넌트

import { Fragment, useState, useEffect } from 'react';

import Users from './Users';
import classes from './UserFinder.module.css';

const DUMMY_USERS = [
  { id: 'u1', name: 'Max' },
  { id: 'u2', name: 'Manuel' },
  { id: 'u3', name: 'Julie' },
];


const UserFinder = () => {
  const [filteredUsers, setFilteredUsers] = useState(DUMMY_USERS);
  const [searchTerm, setSearchTerm] = useState('');

  useEffect(() => {
    setFilteredUsers(
      DUMMY_USERS.filter((user) => user.name.includes(searchTerm))
    );
  }, [searchTerm]);

  const searchChangeHandler = (event) => {
    setSearchTerm(event.target.value);
  };

  return (
    <Fragment>
      <div className={classes.finder}>
        <input type='search' onChange={searchChangeHandler} />
      </div>
      <Users users={filteredUsers} />
    </Fragment>
  );
};

 

 

클래스형 컴포넌트

import { Fragment, useState, useEffect, Component } from 'react'; //Component import

import Users from './Users';
import classes from './UserFinder.module.css';

const DUMMY_USERS = [
  { id: 'u1', name: 'Max' },
  { id: 'u2', name: 'Manuel' },
  { id: 'u3', name: 'Julie' },
];

class UserFinder extends Component {
  constructor() { //state 초기화
    super();
    this.state = {
      filteredUsers: DUMMY_USERS, 
      searchTerm: '',
    };
  }

  componentDidMount() {     // 컴포넌트 초기 렌더링 시 실행
    // HTTP Request ~ Dummy Users 대신할 정보 가져오기
    this.setState({ filteredUsers: DUMMY_USERS });
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.searchTerm !== this.state.searchTerm) {
      this.setState({
        filteredUsers: DUMMY_USERS.filter((user) =>
          user.name.includes(this.state.searchTerm)
        ),
      });
    }
  }

  searchChangeHandler(event) {
    this.setState({ searchTerm: event.target.value });
  }

  render() {
    return (
      <Fragment>
        <div className={classes.finder}>
          <input type='search' onChange={this.searchChangeHandler.bind(this)} />
        </div>
        <Users users={this.state.filteredUsers} />
      </Fragment>
    );
  }

export default UserFinder;

 

useEffect -> ComponentDidUpdtate

ComponentDidUpdtate 안에 바로 this.setState 써주면 무한루프가 발생한다.

  componentDidUpdate() {
    this.setState({
        filteredUsers: DUMMY_USERS.filter((user) =>
          user.name.includes(this.state.searchTerm)
        ),
     });  
  }

setState -> state 변경 -> 컴포넌트가 재실행 -> componentDidUpdate()
   -> setState.. 무한루프

따라서 useEffect에서 의존성배열에 searchTerm을 넣어줬던 것처럼
searchTerm이 변경여부를 갖고 setState해줘야한다.
이를 위해 componentDidUpdate는 이전의 props, 이전의 state  두개의 인자를 전달받는다.
useEffect의 의존성 배열에 넣어준 state의 이전state와 현재 state를 비교해서 달라진 경우에만 if 절 안에서 setState가 실행되게 한다.
그러면 다른 state 변화가 있는 경우에는 setState가 일어나지 않고, 
searchTerm의 변화가 있는 경우에만 if 절 실행되므로 무한루프에 빠지지 않고 state를 변경할 수 있다.
(searchTerm state 변화로 setState가 한번 되면, searchTerm의 state가 또 변하기전까지 if절 실행되지 않기때문에..)

  componentDidUpdate(prevProps, prevState) {
    if (prevState.searchTerm !== this.state.searchTerm) {
      this.setState({
        filteredUsers: DUMMY_USERS.filter((user) =>
          user.name.includes(this.state.searchTerm)
        ),
      });
    }
  }