실전형 리액트 Hooks 10개

김성현·2021년 6월 26일
30

강의

목록 보기
4/9

React-Hook

useInput

검증 기능이 있는 useInput

const useInput = (initialValue, validator) => {
  const [value, setValue] = useState(initialValue);
  const onChange = event => {
    const { target: { value } } = event;
    let willUpdate = true;
    if(typeof validator === "function"){
      willUpdate = validator(value);
    }
    if(willUpdate){
      setValue(value);
    }
  }
  return { value, onChange };
};

export default function App() {
  // validator 함수
  const maxLen = (value) => value.length <= 10;
  const name = useInput("Mr.", maxLen);
  return (
    <div className="App">
      <h1>Hello</h1>
      <input placeholder="Name" {...name}/>
    </div>
  );
}

useTabs

const content = [
  {
    tab: "Section 1",
    content: "I'm the content of the Section 1"
  },
  {
    tab: "Section 2",
    content: "I'm the content of the Section 2"
  }
]

const useTabs = (initialTab, allTabs) => {
  const [currentIndex, setCurrentIndex] = useState(initialTab);
  return {
    currentItem: allTabs[currentIndex],
    changeItem: setCurrentIndex
  }
};

export default function App() {
  const { currentItem, changeItem } = useTabs(0, content);
  return (
    <div className="App">
      {content.map((section, index) => (
        <button onClick={() => changeItem(index)}>{section.tab}</button>
      ))}
      <div>
        { currentItem.content }
      </div>
    </div>
  );
}

useEffect

effect, dependency(배열:적용할 대상들) 인자로 전달

  • dependency가 없는 경우 : 매번 실행
// componentDidMount + componentWillUpdate
useEffect(()=>{
  sayHello()
})
  • dependency가 빈 배열인 경우 : 처음 한번만 실행
// componentDidMount
useEffect(()=>{
  sayHello()
},[])
  • dependency가 있는 경우 : 처음과 dependency안의 값이 바뀔때마다 실행
// componentDidMount + componentWillUpdate
useEffect(()=>{
  sayHello()
},[number])
  • 리턴 함수 => componentWillUnmount에 실행

useTitle

문서 제목 업데이트
처음에 Loading... 이였다가 3초뒤 Home으로 변경

const useTitle = (initialTitle) => {
  const [title, setTitle] = useState(initialTitle);
  const updateTitle = () => {
    const htmlTitle = document.querySelector("title");
    htmlTitle.innerText = title;
  };
  useEffect(updateTitle, [title]);
  return setTitle;
};

export default function App() {
  const titleUpdater = useTitle("Loading...");
  setTimeout(() => titleUpdater("Home"), 3000);
  return (
    <div className="App">
      <h1>Hi</h1>
    </div>
  );
}

useClick

클릭시 함수 실행(= onClick)

const useClick = (onClick) => {
  const element = useRef(); // HTML 태그 연결
  useEffect(() => {
    if (element.current) { // 반드시 current로 접근
      element.current.addEventListener("click", onClick);
    }

    // 반환된 함수는 componentWillUnmount일때 동작(이벤트 리스너 삭제)
    return () => {
      if (element.current) {
        element.current.removeEventListener("click", onClick);
      }
    };
  }, []); // 한번만 실행
  return element;
};

export default function App() {
  const sayHello = () => console.log("say hello");
  const title = useClick(sayHello);
  return (
    <div className="App">
      <h1 ref={title}>Hi</h1>
    </div>
  );
}

useHover

const useHover = (onHover) => {
  const element = useRef();
  useEffect(() => {
    if (element.current) {
      element.current.addEventListener("mouseenter", onHover);
    }
    return () => {
      if (element.current) {
        element.current.removeEventListener("mouseenter", onHover);
      }
    };
  }, []);
  return element;
};

export default function App() {
  const sayHello = () => console.log("say hello");
  const title = useHover(sayHello);
  return (
    <div className="App">
      <h1 ref={title}>Hi</h1>
    </div>
  );
}

useConfirm

사용자가 무언가를 하기전에 확인

const useConfirm = (message = "", onConfirm, onCancel) => {
  const confirmAction = () => {
    if (confirm(message)) {
      onConfirm();
    } else {
      onCancel();
    }
  };
  return confirmAction;
};

usePreventLeave

window 창 닫기 전 알림

const usePreventLeave = () => {
  const listener = (event) => {
    event.preventDefault();
    event.returnValue = ""; // 이걸 넣어야 동작함
  };
  const enablePreve = () => window.addEventListener("beforeunload", listener);
  const disablePreve = () =>
    window.removeEventListener("beforeunload", listener);
  return { enablePreve, disablePreve };
};

useBeforeLeave

마우스가 창을 벗어날 때 함수 실행

const useBeforeLeave = (onBefore) => {
  useEffect(() => {
    document.addEventListener("mouseleave", onBefore);
    return () => document.removeEventListener("mouseleave", onBefore);
  }, []);
};

useFadeIn

const useFadeIn = (duration = 1, delay = 0) => {
  const element = useRef();
  useEffect(() => {
    if (element.current) {
      const { current } = element;
      current.style.transition = `opacity ${duration}s ease-in-out ${delay}s`;
      // delay 시간 뒤에 duration 시간에 걸쳐서 화면에 렌더링
      current.style.opacity = 1;
    }
  }, []);
  return { ref: element, style: { opacity: 0 } };
};

export default function App() {
  const fadeInH1 = useFadeIn(1, 2);
  const fadeInP = useFadeIn(5, 10);
  return (
    <div className="App">
      <h1 {...fadeInH1}>Hello</h1>
      <p {...fadeInP}>lalalalala</p>
    </div>
  );
}

useNetwork

navigator의 online 또는 offline 감지

const useNetwork = (onChange) => {
  const [status, setStatus] = useState(navigator.onLine);
  const handleChange = () => {
    if (typeof onChange === "function") {
      onChange(navigator.onLine);
    }
    setStatus(navigator.onLine);
  };
  useEffect(() => {
    window.addEventListener("online", handleChange);
    window.addEventListener("offline", handleChange);
    return () => {
      window.removeEventListener("online", handleChange);
      window.removeEventListener("offline", handleChange);
    };
  }, []);
  return status;
};

export default function App() {
  const handleNetworkChange = (online) => {
    console.log(online ? "We just went online" : "We are offline");
  };
  const onLine = useNetwork(handleNetworkChange);
  return (
    <div className="App">
      <h1>{onLine ? "Online" : "Offline"}</h1>
    </div>
  );
}

useScroll

const useScroll = () => {
  const [state, setState] = useState({ x: 0, y: 0 });
  const onScroll = () => {
    setState({ y: window.scrollY, x: window.scrollX });
  };
  useEffect(() => {
    window.addEventListener("scroll", onScroll);
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  return state;
};

export default function App() {
  const { y } = useScroll();
  return (
    <div className="App" style={{ height: "1000vh" }}>
      <h1 style={{ position: "fixed", color: y > 100 ? "red" : "blue" }}>Hi</h1>
    </div>
  );
}

useFullscreen

이미지 확대하기

const useFullscreen = (callback) => {
  const element = useRef();

  const triggerFull = () => {
    if (element.current) {
      if (element.current.requestFullscreen) {  // 크롬
        element.current.requestFullscreen();
      } else if (element.current.mozRequestFullScreen) {  /// firefox
        element.current.mozRequestFullScreen();
      } else if (element.current.webkitRequestFullscreen) { // opera
        element.current.webkitRequestFullscreen();
      } else if (element.current.msRequestFullscreen) { // Microsoft
        element.current.msRequestFullscreen();
      }
      callback(true);
    }
  };
  
  const exitFull = () => {
    if (document.exitFullscreen) {
      document.exitFullscreen();
    } else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
    } else if (document.msExitFullscreen) {
      document.msExitFullscreen();
    }
    callback(false);
  };
  return { element, triggerFull, exitFull };
};

export default function App() {
  const onFullS = (isFull) => {
    console.log(isFull ? "We are full" : "We are small");
  };
  const { element, triggerFull, exitFull } = useFullscreen(onFullS);
  return (
    <div className="App" style={{ height: "1000vh" }}>
      <div ref={element}>
        <img src="https://placeimg.com/256/198/any" />
        <button onClick={exitFull}>Exit fullscreen</button>
      </div>
      <button onClick={triggerFull}>Make fullscreen</button>
    </div>
  );
}

useNotification

크롬 우측 상단에 알림 띄우기(notification 허용 필요)

const useNotification = (title, options) => {
  if (!("Notification" in window)) {
    return;
  }
  const fireNotif = () => {
    if (Notification.permission !== "granted") {
      Notification.requestPermission().then((permission) => {
        if (permission === "granted") {
          new Notification(title, options);
        } else {
          return;
        }
      });
    } else {
      new Notification(title, options);
    }
  };
  return fireNotif;
};

export default function App() {
  const triggerNotif = useNotification("my Notification", {
    body: "I love kimchi"
  });
  return (
    <div className="App">
      <button onClick={triggerNotif}>Hello</button>
    </div>
  );
}

useAxios

refetch 가능한 데이터 fetch

import { useEffect, useState } from "react";
import axios from "axios";

const useAxios = (opts, axiosInstance = axios) => {
  const [state, setState] = useState({
    loading: true,
    error: null,
    data: null
  });
  const [trigger, setTrigger] = useState(0);
  const refetch = () => {
    setState({
      ...state,
      loading: true
    });
    setTrigger(Date.now());
  };
  useEffect(() => {
    axiosInstance(opts)
      .then((data) => {
        setState({
          ...state,
          loading: false,
          data
        });
      })
      .catch((error) => {
        setState({ ...state, loading: false, error });
      });
  }, [trigger]);
  return { ...state, refetch };
};

export default function App() {
  const { loading, error, data, refetch } = useAxios({
    url: "https://yts.am/api/v2/list_movies.jsoin" // 현재는 안되는 url
  });
  return (
    <div className="App">
      <h1>{data && data.status}</h1>
      <h2>{loading && "Loading"}</h2>
      <button onClick={refetch}>Refetch</button>
    </div>
  );
}
profile
JS개발자

2개의 댓글

comment-user-thumbnail
2021년 7월 2일

깔끔한 정리 감사합니다!

답글 달기
comment-user-thumbnail
2021년 7월 3일

잘읽고가용 :)

답글 달기