[실전형 리액트 Hooks 10개]

min5x5·2023년 11월 23일
post-thumbnail

#0. Introduction

VSC 대신 codesandbox를 사용할 것이다.

#1. UseState

#1.0 Introduction to useState

React Hooks - 노마드 코더 영상
functional component에서 state를 가질 수 있게 해준다.
리액트 훅을 사용한다면 class component, did mount, render 등을 하지 않아도 된다.
functional programming(함수형 프로그래밍)이 된다.
recompose library로 부터 시작되었다.
hooks은 react의 state machine에 연결하는 기본적인 방법이다.

useState
useState는 항상 2개의 value를 가진 Array를 return한다.

// functional component
const App = () => {
	const [item, setItem] = useState(1);
	const incrementItem = () => setItem(item + 1);
	const decrementItem = () => setItem(item - 1);
}
// class component
// hook을 사용할 수 없다. -> state를 사용할 수 없다.
// 코드 길이가 엄청 길어진다.
class App extends React.Component{
	state = {
    	item: 1
    }
    render() {
    	const {item} = this.state;
        return (
        	<div>...</div>
        )
    }
    incrementItem = () => {
    	this.setState(state => {
        	return {
            	item: state.item + 1
            };
        });
    }
    decrementItem = () => {
    	this.setState(state => {
        	return {
            	item: state.item - 1
            };
        });
    }
}

#1.1 useInput

React는 다른 function에서 event처리를 할 수 있다.
event를 분리된 파일, 다른 entity에 hooking해서 처리할 수 있다.

#1.2 useInput part2

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

const App = () => {
	const maxLen = value => value.length < 10;
    const name = useInput("Mr.", maxLen);
    return (
    	<div>
        	// name의 value, onChange가 shadowing 된다.
        	<input {...name} />          
        </div>
    );
};
    

#1.3 useTabs

setState는 모든 걸 새로고침 해준다. rerender해준다.

import React, { useState } from "react";

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

	if (!allTabs || Array.isArray(allTabs)) {
		return;
	};

	return ({
      currentItem: allTabs[currentIndex],
      changeItem: setCurrentIndex
    });
}

const UseTabsApp = () => {
  const { currentItem, changeItem } = useTabs(0, content);

  return (
    <div>
      {content.map((section, index) => (
          <button onClick{()=>changeItem(index)}> {section.tab}</button>
      ))}
      {currentItem.content}
    </div>
  )
}

export default UseTabsApp;

#2 UseEffect

#2.0 Introduction to useEffect

useEffect는 함수와 deps 2개의 인자를 가진 함수이다.
useEffect()는 componentDidMount(), componentDidUpdate(), componentWillUnmount() 의 역할을 한다.

  • 1번째 인자-function으로서의 effect
    ■ componentDidMount()와 기능이 비슷하다.
  • 2번쨰 인자-deps (dependency)
    ■ useEffect()가 deps리스트에 있는 값이 변할 때만 실행되게 한다.
    ■ componentDidUpdate()와 기능이 비슷하다.

#2.1 useTitle

useEffect를 이용한 hook을 작성할 것이다.
title을 업데이트하는 useTitle이라는 hook을 작성해볼 것!

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

const App = () => {
	const titleUpdater = useTitle("Loading...");
 	setTimeout(() => titleUpdater("Home"), 5000);
    
    return ();
};

#2.2 useClick

references? componenet의 어떤 부분을 선택할 수 있는 방법

const element = useRef();

<div ref={element}></div>
const useClick = (onClick) => {
  const element = useRef();
  useEffect(() => {
    if(typeof onClick !== "function") {
    	return;
  	}
    if(element.current){
      element.current.addEventListener("click", onClick);
    }
    return () => {
      if(element.current){
      element.current.removeEventListener("click", onClick);
    }
  }
  },[])
  return typeof onClick !== "function" ? undefined: element;
};

#2.3 useConfirm & usePreventLeave

useState, useEffect를 사용하지 않기 때문에 실제로 hook은 아닌 useConfirm, usePreventLeave를 만들어 볼 것이다.

const useConfirm = (message="", onConfirm, onCancel) => {
	if (!onConfirm || typeof onConfirm !== "function") {
    	return;
    }
    if (onCancel && typeof onCancel !== "function") {
    	return;
    }
    const confirmAction = () => {
    	if (confirm(message)) {
        	onConfirm();
        } else {
        	onCancel();
        }
    };
    return confirmAction;
}

const App = () => {
	const deleteWorld = () => console.log("deleting...");
    const abort = () => console.log("aborted");
    const confirmDelete = useConfirm("Are u sure?", deleteWorld);
    return (
    	<button onClick={confirmDelete}>Delete</button>
    );
}

beforeunload는 window가 닫히기 전에 function이 실행되는 것을 허락한다.

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

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

#2.4 useBeforeLeave

마우스가 브라우저에서 위로 벗어날 때!

const useBeforeLeave = (onBefore) => {
	if(typeof onBefore !== "function") {
    	return;
    }
	const handle = event => {
    	const {clientY} = event;
        if (clientY) <= 0) {
        	onBefore();
		}
	};
	useEffect(() => {
    	document.addEventListener("mouseleave", handle);
        return () => document.removeEventListener("mouseleave", handle);
    }, []);
}
    
const App = () => {
	const begForLife = () => console.log("plz don't leave");
    useBeforeLeave(begForLife);
	return (
    	...
    );
}

#2.5 useFadeIn & useNetwork

useFadeIn

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

const App = () => {
	const fadeInH1 = useFadeIn();
    const fadeInP = useFadeIn();
    return (
    	<div>
        	<h1 {...fadeInH1}>hello</h1>
        </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);
        // cleanup everything when componentWillUnmount
        () => {
        	window.removeEventListener("online", handleChange);
            window.removeEventListener("offline", handleChange);
        }
    }, []);
    return status;
}

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

#2.6 useScroll & useFullscreen

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

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

useFullscreen 브라우저에 따라 fullscreen 실행함수명이 다르다.

const useFullscreen = (callback) => {
  const element = useRef();
  const runCb = (isFull) => {
    if (callback && typeof callback === "function") {
      callback(isFull);
    }
  };
  const triggerFull = () => {
    if (element.current) {
      if (element.current.requestFullscreen) {  //chrome
        element.current.requestFullscreen();
      } else if (element.current.webkitRequestFullscreen) {   //opera
        element.current.webkitRequestFullscreen();
      } else if (element.current.msRequestFullscreen) {   //Microsoft
        element.current.msRequestFullscreen();
      } else if (element.current.mozRequestFullScreen) {   //firefox
        element.current.mozRequestFullScreen();
      }
      runCb(true);
    }
  };
  const exitFull = () => {
    if (document.exitFullscreen) {
      document.exitFullscreen();
    } else if (document.webkitRequestFullscreen) {
      document.webkitRequestFullscreen();
    } else if (document.msRequestFullscreen) {
      document.msRequestFullscreen();
    } else if (document.mozRequestFullScreen) {
      document.mozRequestFullScreen();
    }
    runCb(false);
  };
  return {element, triggerFull, exitFull};
};


const App = () => {
	const onFullS = (isFull) => {
    	console.log(isFull ? "we are full": "we are small")
    }
	const { element, triggerFull, exitFull } = useFullscreen();
    return (
    	<div style={{ height: "1000vh" }}>
        	<div ref={element}>
        		<img src="..."></img>
            	<button onClick={trigerFull}>make fullscreen</button>
            </div>
            <button onClick={exitFull}>exit fullscreen</button>

        </div>
    );
}

#2.7 useNotification

https://developer.mozilla.org/en-US/docs/Web/API/notification 참고해서 만들어나가면 된다.

const useNotification = (title, options)  => {
  if (!("Notification" in window)) {
    return;
  }
  const fireNotif = () => {
    if (Notification.permission !== "granted") { // permission이 부여되지 않은 경우
      // promise를 준다.
      Notification.requestPermission().then((permission) => {
        if (permission === "granted") {
          new Notification(title, options);
        } else return;
      });
    }
  };
  return fireNotif;
  
}

const App = () => {
  const triggerNotif = useNotification(
    "Hello, Can I hack your Computer?",
    {body: "If you agree, I will hack 😈"}
  );
  return (
    <div className="App" style={{ height: "1000vh" }}>
      <button onClick={triggerNotif}>Hello</button>
    </div>
  );
};

#2.8 useAxios

axios는 http request를 만드는 것이다.
axios는 instance 만드는 것을 허용하고, configure할 수 있고, 헤더를 보낼 수 있다.

// useAxios.js
import dafaultAxios from "axios";
import {useState, useEffect} from "react";

const useAxios = (opts, axiosInstance = dafaultAxios) => {
    const [state, setState] = useState({
        loading: true,
        error: null,
        data: null
    });
    const [trigger, setTrigger] = useState(0);
    const refetch = () => {
        setState({
            ...state,
            loading: true
        });
        setTrigger(Date.now());
        // trigger로 Date.now()를 사용한다!
    };
    useEffect(() => {
        axiosInstance(opts)
            .then((data) => {
                setState({
                    ...state,
                    loading: false,
                    data
                });
            })
            .catch(error => {
                setState({...state, loading: false, error});
            });
    }, [trigger]); // trigger가 바뀔때마다 useEffect가 실행된다.
    if (!opts.url) {
        return;
    }
    return {...state, refetch};
};

export default useAxios;
// App.js
import ReactDOM from "react-dom";
import useAxios from "./useAxios/useAxios.js";

const App = () => {
  const {loading, data, error, refetch} = useAxios({
    url:"https://yts.mx/api/v2/list_movies.json"
  });
  console.log(`loading: ${loading}\ndata: ${JSON.stringify(data)}\n`)
  return (
    <div className="App" style={{ height: "1000vh" }}>
      <h1>{data && data.status}</h1>
      <h2>{loading && "Loading"}</h2>
      <button onClick={refetch}>Refetch</button>
    </div>
  );
};

#2.9 Conclusions

#2.10 Publishing to NPM

#2.11 What to learn Next

profile
삶에 변화를 만드는 개발자

0개의 댓글