[리액트] React Hook

김학재·2021년 4월 27일
0

리액트

목록 보기
4/8
post-thumbnail

0427

리액트 훅이란
클래스 기반 컴포넌트의 장점(내부 state, 생명주기 메소드 etc)을 함수형 컴포넌트로 가져오려는 리액트의 시도

  • 클래스 컴포넌트, lifecycle hooks, this의 필요성이 사라진다.
  • 공통 기능을 커스텀 hook으로 만들어서 로직을 재사용하기 쉬워진다.
  • 컴포넌트 자체에서 로직을 분리할 수 있어 읽기 쉽고 테스트하기 쉬운 코드를 작성할 수 있다.

useState
useState는 항상 2개의 value를 return

// 이런 식으로 사용
const [item, setItem] = useState(1);

// item만 쓰고 싶다면
const [item] = useState(1)[0];

useInput
아래와 같은 방식으로 hook처럼 동작하게 할 수 있다.
validator를 넣음으로서 조건에 맞는 경우에만 name을 업데이트함

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

    if (willUpdate) {
      setValue(value);
    }
  }
  
  return {value, onChange};
}

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

0428

useEffect
useEffectcomponentDidMount,componentWillUnmount, componentDidUpdate의 역할을 수행한다.
useEffect는 2개의 인자를 받는다.
첫번째는 function으로서의 effect
두번째는 deps : 만약 deps가 있다면 effect는 리스트에 있는 값일 때만 값이 변하도록 활성화

useEffect(sayHello, [number]);
/*
number가 변할 때에만 sayHello가 실행된다.
*/

useTitle
문서 제목 업데이트해주는 hook
보통 helmet을 쓴다

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

~~
// App 내에서 이런 식으로 사용 가능
const titleUpdater = useTitle("Loading...");
setTimeout(() => titleUpdater("home"), 3000);

0429

useRef
reference는 기본적으로 우리의 Component의 어떤 부분을 선택할 수 있는 방법
document.getElementByID()와 동등함
react의 모든 Component는 reference element를 가진다

export default function App() {
  const potato = useRef();
  // setTimeout(() => potato.current?.focus(), 3000)
  useEffect(() => {
    setTimeout(() => potato.current.focus(), 3000)
  })
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <input ref={potato} placeholder="test"></input> // reference 갖게 한다
    </div>
  );
}
// useClick 사용해서 useRef() 만들고
const useClick = (onClick) => {
  const element = useRef(); // 얘가 제일 중요
  useEffect(() => {
    if (element.current) {
      element.current.addEventListener('click', onClick);
    }
    // 아래 function은 componentWillUnmount 때 호출
    return () => {
      if (element.current) {
        element.current.removeEventListener('click', onClick);
      }
    }
  }, []) // 이 dependency가 없으면 매번 update될 때마다 eventListener가 추가된다
  // componentDidMount때 단 한번만 실행
  return element; // 같은 reference를 return
}

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

componentWillUnmount 될 때 eventListener를 지워야 한다.
function을 리턴받았다면 그 function은 componentWillUnmount로부터 호출된 것
component가 mount 되지 않았을 때 eventListener가 배치되게 하고 싶지 않다

0430

useConfirm
사용자가 클릭했을 때 confirm

// 2. 얘가 실행되고
const useConfirm = (message = "", onConfirm, onCancel) => {
  if (!onConfirm || typeof onConfirm !== "function") {
    return;
  }
  if (onCancel && typeof onCancel !== "function") {
    return;
  }
  const confirmAction = () => {
    if (window.confirm(message)) { // 3. 우리가 true라고 하면
      onConfirm(); // 4. 콜백 실행
    } else {
      onCancel();
    }
  }
  return confirmAction;
}

function App() {
  const deleteWorld = () => console.log('Deleting the world!');
  const abort = () => console.log("Aborted");
  const confirmDelete = useConfirm("Are you sure", deleteWorld, abort);
  return (
    <div className="hello">
      <h1>Hello React Hook</h1>
      <button onClick={confirmDelete}>Delete the world</button> // 1. 버튼 클릭하면
    </div>
  );
}

usePreventLeave
사용자가 페이지 떠나는 걸 방지

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

function App() {
  const {enablePrevent, disablePrevent} = usePreventLeave();
  return (
    <div className="hello">
      <h1>Hello React Hook</h1>
      <button onClick={enablePrevent}>Protect</button>
      <button onClick={disablePrevent}>Unprotect</button>
    </div>
  );
}

beforeunload는 window가 닫히기 전에 funciton이 실행되는 걸 허락한다.

beforeunload 이벤트를 사용하면 사용자가 페이지를 떠날 때 정말로 떠날 것인지 묻는 확인 대화 상자를 표시할 수 있습니다. 사용자가 확인을 누를 경우 브라우저는 새로운 페이지로 탐색하고, 취소할 경우 탐색을 취소하고 현재 페이지에 머무릅니다.

0503

useBeforeLeave
사용자 마우스가 떠나는 걸 방지

const useBeforeLeave = (onBefore) => {
  const handle = (event) => {
    const {clientY} = event;
    if (clientY <= 0) { // 마우스가 현재 창 떠날 때
      onBefore();
    }
  };
  useEffect(() => {
    if (typeof onBefore === "function") {
      document.addEventListener("mouseleave", handle);
      return () => document.removeEventListener("mouseleave", handle);
    } else {
      return;
    }
  }, []);
}

~~~
  
const begForLife = () => console.log("Plz don't leave");
useBeforeLeave(begForLife);

useFadeIn
나타나는 효과 주기

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

function App() {
  const fadeInH1 = useFadeIn(2, 2);
  const fadeInP = useFadeIn(5, 10);
  return (
    <div className="hello">
      <h1 {...fadeInH1}>Hello</h1>
      <p {...fadeInP}>lorem ipsum lalalla</p>
    </div>
  );
}

useNetwork
인터넷 연결 확인하기

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;
}

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

0504

useScroll
사용자의 스크롤 동작 감지

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

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

useFullscreen
전체 화면 만들고 사용자에게 알려주기

const useFullscreen = (callback) => {
  const element = useRef();
  const triggerFull = () => {
    if (element.current) {
      element.current.requestFullscreen();
      if(callback && typeof callback === "function") {
        callback(true);
      }
    }
  }
  const exitFull = () => {
    document.exitFullscreen();
    if(callback && typeof callback === "function") {
      callback(false);
    }
  }
  return {element, triggerFull, exitFull};
}

function App() {
  const onFulls = (isFull) => {
    console.log(isFull ? "We are full" : "We are small");
  }
  const {element, triggerFull, exitFull} = useFullscreen(onFulls);
  return (
    <div className="hello">
      Hello
      <div ref={element}>
        <img 
          src="이미지 경로"
        />
        <button onClick={exitFull}>Exit fullscreen</button>
      </div>
      <button onClick={triggerFull}>Make fullscreen</button>
    </div>
  );
}

useNotification
알람

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;
}

function App() {
  const triggerNotif = useNotification("알람 제목", {
    body: "알람 메시지"
  });
  return (
    <div className="hello">
      <button onClick={triggerNotif}>Hello</button>
    </div>
  );
}
profile
YOU ARE BREATHTAKING

0개의 댓글