Hook3_Hook, ClassComponent 비교

Jun 23rd 2020 by jyoon

모든 코드는 git repository에서 확인 가능합니다.

3.2 componentDidUpdate 메서드

  • 코드 5-34 usePrevious 커스텀 훅

    import { useEffect, useRef } from 'react';
    //POINT1
    export default function(value) {
      //POINT2
      const valueRef = useRef();
      //POINT3
      useEffect(
        () => {
          valueRef.current = value;
        },
        [ value ]
      );
      //POINT4
      return valueRef.current || '';
    }
    • ⭐️ usePrevious 설명

      • 코드 5-22 참고
      • POINT 1. 매개변수로 현재 값을 받는다.
      • POINT 2. 이전 값을 기억하기 위해 useRef 훅을 이용
      • POINT 3. 렌더링 후 현재 값을 이전 값으로 만든다.
      • POINT 4. 이전 값을 반환
    • ⭐️ input에 입력시 수행 순서

      • debugger 순서로 callstack순서를 확인해보자(step 1,2,3 확인)
      • ⭐️ useState의 setxxx function이후 가 일어나면
        setxxx functino이 있는 function 전체 다시 수행된다.
        그리고
        useEffect가 있다면 useEffect가 비동기로 수행된다.
  • 코드 5-35 훅으로 변환할 componentDidUpdate 메서드

      import React from 'react';
    
      class Profile extends React.Component {
        state = { name: this.props.name };
        componentDidUpdate(prevProps) {
          const { userId, name } = this.props;
          if (prevProps.userId !== userId) {
            this.setState({ name });
          }
        }
        render() {
          return (
            <div>
              ...
            </div>
          );
        }
      }
    
      export default Profile;
  • 코드 5-36 componentDidUpdate 메서드를 훅으로 변환하기

    userIdd가 변경된 경우 name 상탯값을 새로운 사용자 이름으로 변경

    • POINT1. userPrevious 훅을 이용해서 이전 userId를 저장
    • POINT2. 마운트 여부를 useRef훅으로 관리
    • POINT3. userIdd가 변경된 경우 name 상탯값을 새로운 사용자 이름으로 변경
      import React, { useState, useEffect, useRef } from 'react';
      import usePrevious from './customHook/usePrevious';
    
      //props = {id name, age, date}
      export default function({ id, name, age, date }) {
        const [ userName, setUserName ] = useState(name);
        const [ userId, setUserId ] = useState(id);
        //POINT1
        const prevUserId = usePrevious(userId);
        //POINT2
        const isMountedRef = useRef(false);
    
        useEffect(() => {
          if (isMountedRef.current) {
            //POINT3
            if (prevUserId !== userId) {
              setUserName(userName);
            }
          } else {
            isMountedRef.current = true;
          }
        });
    
        const onClick = () => {
          setUserName('otherJyoon');
          setUserId('okwoyjy');
        };
    
        return (
          <div>
            <div>
              <h1> 예제 </h1>
              {(backtic)this is current userName: ${userName}(backtic)}
              <br />
              <button onClick={onClick}>버튼으로 componentDidupdate 테스트 </button>
            </div>
          </div>
        );
      }
  • 코드 5-37 useOnUpdate 커스텀 훅

      import { useRef, useEffect } from 'react';
    
      export default function(func) {
        const isMountedRef = useRef(false);
        useEffect(() => {
          if (isMountedRef.current) {
            func();
          } else {
            isMountedRef.current = true;
          }
        });
      }
    • 마운트가 됐을때 넘겨 받은 function을 수행해주는 훅

3.3 getDerivedStateFromProps 메서드

  • 코드 5-38 훅으로 변경할 getDerivedStateFromProps 정적 메서드

      import React from 'react';
    
      class SpeedIndicator extends React.Component {
        state = { isFaster: false, prevSpeed: 0 };
        static getDerivedStateFromProps(props, state) {
          if (props.speed !== state.prevSpeed) {
            return {
              isFaster: props.speed > state.prevSpeed,
              prevSpeed: props.speed
            };
          }
          return null;
        }
    
        render() {
          const { isFaster, prevSpeed } = this.state;
          const { speed } = this.props;
          return (
            <div>
              <p>current spped: {speed}</p>
              <p>getting faster?: {isFaster ? 'yes' : 'no'}</p>
            </div>
          );
        }
      }
    
      class car extends React.Component {
        state = {
          speed: 0,
          enterSpeed: 0
        };
    
        handleChange = (e) => {
          console.log('### handleChange: ');
          this.setState({ speed: e.target.value });
        };
    
        handleSubmit = (e) => {
          console.log('### handleSubmit: ');
          const { speed } = this.state;
          debugger;
          this.setState({ enterSpeed: speed });
          e.preventDefault();
        };
    
        render() {
          const { speed, enterSpeed } = this.state;
          return (
            <div>
              이건차다 스피드 입력해봐
              <form onSubmit={this.handleSubmit.bind(this)}>
                <input type='text' value={speed} onChange={this.handleChange.bind(this)} />
                <br />
                <button type='submit'>enter</button>
              </form>
              <br />
              <SpeedIndicator speed={enterSpeed} />
            </div>
          );
        }
      }
    
      export default car;
  • 코드 5-39 getDerivedStateFromProps 정적 메서드를 훅으로 변경하기

      import React, { useState, useEffect } from "react";
    
      function SpeedIndicator({ speed }) {
        //POINT1
        const [isFaster, setIsFaster] = useState(false);
        const [prevSpeed, setPrevSpeed] = useState(0);
        if (speed !== prevSpeed) {
          setIsFaster(speed > prevSpeed);
          setPrevSpeed(speed);
        }
        return <p>gettin faster?: {isFaster ? "yes" : "no"}</p>;
      }
    
      export default SpeedIndicator;
    • speed 속성값이 변경되면 렌더링 과정에서 바로 상탯값을 변경
    • 리액트는 렌더 함수에서 상탯값을 변경하면 변경된 상탯값으로 렌더 함수를 다시 호출 한다.
    • getDerivedStateFromProps 정적 메서드를 사용한 방법 보다는 조금비효율 적인 측면이 있지만 돔을 변경하기 전에 발생하는 연산이므로 성능에 크게 영향을 주지 않는다.
    • 주의

      • 렌더 함수가 무한대로 호출될 수 있다.
      • 코드5-39에서 작성한 prevSpeed를 useState훅으로 관리하지 않고 이전에 작성한 usePreviou 커스터 훅으로 관리했다면 렌더함수가 무한대로 호출된다.

3.4 forceUpdate 메서드

  • 코드 5-40 useReducer 훅을 이용해서 forceUpdate 메서드를 구현하기

      import React, { useReducer } from 'react';
    
      function MyComponent() {
        //POINT1
        const [ _, forceUpdate ] = useReducer((s) => s + 1, 0);
        function onClick() {
          forceUpdate();
        }
      }
    • forceUpdate 메서드의 사용은 지양해야 하지만 필요한 경우 훅으로 구현할 수 있다.
    • POINT1. forceUpdate 함수를 호출하면 상탯값이 항상 변경되므로 클래스형 컴포넌트의 forceUpdate 메서드 처럼 동작한다.