TIL 22-05-07

thisisyjin·2022년 5월 7일
0

TIL 📝

목록 보기
46/113

React.js

Today I Learned ... react.js

🙋‍♂️ *Reference Lecture

🙋‍ My Dev Blog


React Hooks 강의

Hooks

useEffect

useHover 생성

-> 지난번에 만들었던 useClick에서 이벤트만 mouseenter로 바꿔준 것.

import { useEffect } from "react";

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

export default useHover;

함수형 프로그래밍

useConfirm

  • 사용하자 행동을 하기전에 confirm 창을 띄워서 확인하는 동작.
const useConfirm = (message, callback) => {
  if (typeof callback !== "function") {
    return;
  }
  const confirmAction = () => {
    if (confirm(message)) { // 확인 누르면 콜백 실행
      callback();
    }
  };
  return confirmAction;
};

const App = () => {
  const deleteWorld = () => console.log("delete world!");
  const confirmDelete = useConfirm("Are you sure?", deleteWorld);

  return (
    <div>
      <button onClick={confirmDelete}>delete the world</button>
    </div>
  );
};

confirm('message') 함수를 호출하여 확인을 누르면 true를 반환함.
-> if문의 조건을 충족하여 callback이 실행됨.

+) 취소를 눌렀을때 로직도 추가하면 다음과 같다.

const useConfirm = (message, onConfirm, onCancel) => {
  if (!onConfirm && typeof onConfirm !== "function") {
    return;
  }
  if (onCancel && typeof onCancel !== "function") {
    return;
  }
  const confirmAction = () => {
    if (window.confirm(message)) {
      // 확인 누르면 콜백 실행
      onConfirm();
    } else {
      onCancel();
    }
  };
  return confirmAction;
};

-> useConfirm의 세번째 인자로 onCancel을 넣어줌.
-> 기존 callback은 구분을 위해 onConfirm으로 바꿔줌.

✅ validation check

if (!onConfirm || typeof onConfirm !== "function") {
    return;
  }

if (onCancel && typeof onCancel !== "function") {
    return;
  }
  • onConfirm은 반드시 있어야 하므로 !onConfirm이면 return되게 해야 하고,
    또는 함수가 아니면 return되게 해야 한다.

  • onCancel은 반드시 있어야 하는 것은 아니므로 그냥 onCancel이 함수인지만 검사하면 된다.


usePreventLeave

  • window 객체의 이벤트인 beforeunload 를 이용함.
  • e.preventDefault()를 해서 창 닫기를 멈추고,
  • e.returnValue = "" 를 해줌.

    참고 - returnValue는 권장하지 X. (크롬에서만 지원) - mdn

const usePreventLeave = () => {
  const listener = (e) => {
    e.preventDefault();
    e.returnValue = "";
  };
  const enablePrevent = () => {
    window.addEventListener("beforeunload", listener);
  };
  const disablePrevent = () => {
    window.removeEventListener("beforeunload", listener);
  };
  return { enablePrevent, disablePrevent };
};

const App = () => {
  const { enablePrevent, disablePrevent } = usePreventLeave();
  return (
    <div>
      <button onClick={enablePrevent}>protect</button>
      <button onClick={disablePrevent}>unprotect</button>
    </div>
  );
};

protect 버튼을 클릭하고 창을 닫으면 아래와 같은 창이 뜬다.


useBeforeLeave

  • useEffect 사용
  • 탭을 닫을때 실행되는 function.
    -> 마우스가 탭을 벗어날 때 이벤트 발생.
const useBeforeLeave = (onBefore) => {
  if (typeof onBefore !== "function") {
    return;
  }
  const handle = () => {
    onBefore();
  };

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

const App = () => {
  const begForLife = () => console.log("plz dont leave");
  useBeforeLeave(begForLife);

  return (
    <div>
      <h1>hello</h1>
    </div>
  );
};

+) improveMent

->
mouseEvent를 이용하여 많은 정보를 알 수 있다.

  • 이중에서, clientY를 보면 0보다 작은 경우에 (화면을 닫기위해 상단바로 마우스를 올리므로)
    함수를 실행하도록 해주자.

참고 - clientX, offsetX, screenX의 차이

  • clientX: 사용자의 브라우저 화면 기준
  • offsetX: 이벤트 대상 DOM 객체 기준
  • screenX: 모니터 화면 기준
const handle = (e) => {
    const { clientY } = e;
    if(clientY <= 0) onBefore();
  };

event.clientY가 0보다 작거나 같을때,
(0일때도 포함됨) -> onBefore을 실행함.

이제 마우스가 창을 벗어날 때 (document.mouseleave), clientY가 0 이하일때만 onBefore 함수가 실행됨.
(그 외의 경우에는 전부 X)

전체 코드

const useBeforeLeave = (onBefore) => {
  if (typeof onBefore !== "function") {
    return;
  }

  const handle = (e) => {
    const { clientY } = e;
    if (clientY <= 0) onBefore();
  };

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

const App = () => {
  const begForLife = () => console.log("plz dont leave");
  useBeforeLeave(begForLife);

  return (
    <div>
      <h1>hello</h1>
    </div>
  );
};

useFadeIn

  • element가 자동으로 서서히 나타나도록 함.
  • css로도 만들 수 있지만,
    animation을 hook에 포함시킬 수 있는 방법을 알아보자.
  • useRef, useEffect 사용.
const useFadeIn = (duration = 1, delay = 0) => {
  const element = useRef();

  if (typeof duration !== "number" || typeof delay !== "number") {
    return;
  }

  useEffect(() => {
    if (element.current) {
      const { current } = element;
      current.style.transition = `opacity ${duration}s ease-in ${delay}s`;
      current.style.opacity = 1;
    }
  }, []);
  return { ref: element, style: { opacity: 0 } };
};

const App = () => {
  const fadeInH1 = useFadeIn(2, 2);
  const fadeInP = useFadeIn(4, 6);
  return (
    <div>
      <h1 {...fadeInH1}>hello</h1>
      <p {...fadeInP}>lorem ipsum lalallala</p>
    </div>
  );
};
  • 만약 transition-timing도 설정하게 하려면,
    세번째 인자로 ease-in, ease-out, ease-in-out 등을 받게 해야함.
    -> 유효성 검사시 어떻게 할지.
if (!['ease', 'ease-in', 'ease-out', 'ease-in-out'].includes(timing)) return;

const fadeInSpan = useFadeIn(3, 5, 'ease-in'); // 통과

const fadeInDiv = useFadeIn(3, 5, 'easy'); // 걸러짐. (효과X)

-> 기본값으로 ease를 주는게 좋을듯?
오타가 나더라도 ease가 적용되게 하려면
-> else에다가 timing = 'ease' 대입하면 됨.


useNetwork

  • navigator.onLine을 이용함.
  • window 객체의 이벤트인 onlineoffline을 이용함.
  • mdn 문서 참고.
  • Returns the online status of the browser. The property returns a boolean value,
  • with true meaning online and false meaning offline.
const useNetwork = (onChange) => {
  const [status, setStatus] = useState(navigator.onLine); // true 또는 false
  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;
};

const App = () => {
  const handleNetworkChange = (online) => {
    console.log(online ? "online status" : "offline status");
  };

  const onLine = useNetwork(handleNetworkChange);

  return (
    <div>
      <h1>{onLine ? "Online!" : "Offline"}</h1>
    </div>
  );
};

profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글