Hook4_Class Component 고려한 CustomHook 작성법

Jun 23rd 2020 by jyoon

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

4.1 커스텀 훅의 반환값이 없는 경우

  • 코드 5-41 디바운스 기능을 제공하는 useDebounce 훅의 구현 및 사용

      import React, { useState, useEffect } from "react";
    
        //POINT1
        function useDebounce({ callback, ms, args }) {
          useEffect(() => {
            const id = setTimeout(callback, ms);
            return () => clearTimeout(id);
          });
        }
    
        function Profile() {
          let [name, setName] = useState("");
          let [nameTemp, setNameTemp] = useState("");
          //POINT2
          useDebounce({
            callback: () => setName(nameTemp),
            ms: 1000,
            args: [nameTemp],
          });
    
          return (
            <div>
              <div>
                <h1> 예제 </h1>
                <p>{name}</p>
                <input
                  type="text"
                  value={nameTemp}
                  onChange={(e) => setNameTemp(e.currentTarget.value)}
                ></input>
              </div>
            </div>
          );
        }
      export default Profile;
    • 반환값이 없는 커스텀 훅을 클래스형 컴포넌트에서 사용하는 방법을 알아보자
    • 아래 예제 설명

      • 디바운스 기능을 제공하는 커스텀 훅과 그것을 사용하는 코드
    • 코드 설명

      • POINT1 : ms 시간 동안 args가 변경되지 않으면 callback 함수를 호출하는 커스텀 훅
      • POINT2 : 사용자가 입력하는 문자역을 nameTemp에 기록하다가 1초간 입력이 없으면 name에 저장
    • input을 입력시 1초 동안 입력하지 않으면 name update된다.
  • 클래스형 컴포넌트에서 useDebounce 훅 이용하기

    • 코드 5-42 useDebounce 훅의 래퍼 컴포넌트

      import React, { useEffect } from 'react';
      
      function useDebounce({ callback, ms, args }) {
      useEffect(() => {
        const id = setTimeout(callback, ms);
        return () => clearTimeout(id);
      });
      }
      
      function Debounce({ children, ...props }) {
      useDebounce(props);
      return children;
      }
      
      class Profile extends React.Component {
      state = { name: '', nameTemp: '' };
      render() {
        const { name, nameTemp } = this.state;
        return (
          <div>
            <div>
              <h1> 예제 </h1>
              <Debounce callback={() => this.setState({ name: nameTemp })} ms={1000} args={[ nameTemp ]}>
                <div>
                  <p>{name}</p>
                  <input
                    type='text'
                    value={nameTemp}
                    onChange={(e) => this.setState({ nameTemp: e.currentTarget.value })}
                  />
                </div>
              </Debounce>
            </div>
          </div>
        );
      }
      }
      
      export default Profile;
      • 클래스형 컴포넌트에서 useDebounce 훅 이용하는 코드이다.
      • useDebounce 처럼 반환값이 없는 훅은 간단한 래퍼 컴포넌트를 통해서 클래스형 컴포넌트에서 사용할 수 있다.
      • 코드 5-43 에서 useDebounce를 사용하는 클래스형 컴포넌트를 확인할 수 있다.
      • input을 입력시 1초 동안 입력하지 않으면 name update된다.
  • 코드 5-43 클래스형 컴포넌트에서 Debounce 컴포넌트를 사용하기

      import React, { useEffect } from "react";
    
      function useDebounce({ callback, ms, args }) {
        useEffect(() => {
          const id = setTimeout(callback, ms);
          return () => clearTimeout(id);
        });
      }
    
      function Debounce({ children, ...props }) {
        useDebounce(props);
        return children;
      }
    
      class Profile extends React.Component {
        state = { name: "", nameTemp: "" };
        render() {
          const { name, nameTemp } = this.state;
          return (
                <Debounce
                  callback={() => this.setState({ name: nameTemp })}
                  ms={1000}
                  args={[nameTemp]}>
                  <div>
                    <p>this is name: {name}</p>
                    <input
                      type="text"
                      value={nameTemp}
                      onChange={(e) =>
                        this.setState({ nameTemp: e.currentTarget.value })
                      }
                    ></input>
                  </div>
                </Debounce>
              </div>
            </div>
          );
        }
      }
    
      export default Profile;
    • 클래스형 컴포넌트에서 useDebounce 훅 이용하는 코드이다.

      • useDebounce 처럼 반환값이 없는 훅은 간단한 래퍼 컴포넌트를 통해서 클래스형 컴포넌트에서 사용할 수 있다.
    • input을 입력시 1초 동안 입력하지 않으면 name update된다.

4.2 커스텀 훅의 반환값이 있는 경우

  1. 마운트 상태를 관리하는 useHasMounted 훅

    • 마운트 상태를 관리하는 useHasMounted
    • 코드 5-14 useHasMounted처럼 반환값이 잇는 훅은 고차 컴포넌트 또는 렌더 속성값으로 반환값을 전달할 수 있다.
    • 다음은 'useHasMounted훅의 기능을 제공'하기 위해 구현한 '고차 컴포넌트'와 '렌더 속성'이다.
  2. 코드 5-44 useHasMounted 훅의 기능을 제공하기 위해 구현한 고차 컴포넌트와 렌더 속성값

    • 고차 컴포넌트 예
      import React from 'react';
      import useHasMounted from './customHook/useHasMounted';
    
      function withHasMounted(Component) {
        return function(props) {
          const hasMounted = useHasMounted();
          return <Component {...props} hasMounted={hasMounted} />;
        };
      }
    
      class MyComponent extends React.Component {
        render() {
          const { hasMounted } = this.props;
          return (
            <div>
              <p> # 클래스 컴포넌트에서 useHasMounted 훅 사용 테스트 입니다.</p>
              <p>* 마운트가 됐나요? </p>
              <p>{hasMounted ? 'yes' : 'no'}</p>
            </div>
          );
        }
      }
    
      export default withHasMounted(MyComponent);
    • 렌더 속성값 예
      import React from 'react';
      import useHasMounted from './customHook/useHasMounted';
    
      function HasMounted({ children }) {
        const hasMounted = useHasMounted();
        return children(hasMounted);
      }
    
      class MyComponent extends React.Component {
        render() {
          const { hasMounted } = this.props;
          return (
            <div>
              <p> # 클래스 컴포넌트에서 useHasMounted 훅 사용 테스트 입니다.</p>
              <p>* 마운트가 됐나요? </p>
              <p>{hasMounted ? 'yes' : 'no'}</p>
            </div>
          );
        }
      }
    
      export default function() {
        return <HasMounted>{(data) => <MyComponent hasMounted={data} />}</HasMounted>;
      }
  3. 클래스형 컴포넌트에서 useHasMounted 훅 이용하기
  4. 코드 5-45 클래스형 컴포넌트에서 withHasMounted 고차 컴포넌트 사용하기

    • 코드 5-44의 고차 컴포넌트 예, 렌더 속성값 예 참고
      import React from 'react';
      import useHasMounted from './customHook/useHasMounted';
      function HasMounted({ children }) {
        const hasMounted = useHasMounted();
        return children(hasMounted);
      }
    
      function withHasMounted(Component) {
        return function(props) {
          const hasMounted = useHasMounted();
          return <Component {...props} hasMounted={hasMounted} />;
        };
      }
    
      class MyComponent extends React.Component {
        render() {
          const { hasMounted } = this.props;
          return (
            <div>
              <p> # 클래스 컴포넌트에서 useHasMounted 훅 사용 테스트 입니다.</p>
              <p>* 마운트가 됐나요? </p>
              <p>{hasMounted ? 'yes' : 'no'}</p>
            </div>
          );
        }
      }
    
      export default function() {
        return <HasMounted>{(data) => <MyComponent hasMounted={data} />}</HasMounted>;
      }