패스트캠퍼스 한 번에 끝내는 프론트엔드 개발(Online) 강의 정리
앞에서 배운 useState
와 useEffect
를 활용하여, 나만의 Hooks
만들기
반복되는 로직
이 자주 발생함 (비슷한 코드 반복)useState
, useEffect
, useInput
, useFetch
, useReducer
, useCallback
등 기존 내장 Hooks 사용해 원하는 기능을 구현하고, 컴포넌트에서 사용하고 싶은 값들을 반환해주면 됨배열 혹은 오브젝트를 return
한다.return [value];
혹은 return {value};
한다. 배열의 수와 오브젝트의 수는 필요에 따라 추가 가능)Custom Hook의 이름은 무조건 use로 시작해야한다
import { useState } from 'react';
const useFetch = (initialUrl: string) => {
const [url, setUrl] = useState(initialUrl);
return [url, setUrl];
};
export default useFetch;
import { useState, useEffect } from 'react';
export default function useWindowWith() {
const [width, setWidth] = React.useState(window.innerWidth); // 현재 브라우저 가로 값 가져옴
useEffect( () => {
window.addEventListner("resize", () => { // resize event
setWidth(window.innerWidth);
});
window.addEventListner("resize", resize); // 실행
return () => {
window.removeEventListner("resize", resize); // state갑 변경 되어 return값이 width를 다시 렌더
}
}, []); // dependency
return width;
}
import Example1 from "./components/Example1";
import Example2 from "./components/Example2";
import Example3 from "./components/Example3";
import Example4 from "./components/Example4";
import Example5 from "./components/Example5";
import useWindowWith "./hooks/useWindowWith"; // 추가
function App() {
const width = useWindowWith(); // 공통 로직으로 사용할 수 있도록 만든 Custom Hook
return (
<div className="App">
<header className="App-header">
<Example1 />
<Example2 />
<Example3 />
<Example4 />
<Example5 />
{ /* width 표시 */ }
{width} // 브라우저 크기를 조정할 때 사이즈 값이 들어옴
</header>
</div>
);
}
로직을 따로 분리하여 사용하고, Hook 과 HOC의 차이점을 알기
useHasMounted
(Mounted됐을 때 알려줌)withHasMounted
(인자로 컴포넌트를 넣었을 때 HasMounted라는 props가 들어간 새로운 컴포넌트 생성)import React from 'react';
export default function withHasMounted(Component) { // 인자로 Component
class NewCompnent extends React.Component { // 새로운 컴포넌트 생성
state = { hasMounted: false, };
render() {
const { hasMounted } = this.state;
return <Component {...this.props} hasMounted={hasMounted} />; // hsMounted를 Props로 내려줌
}
// 처음 렌더된 후 해당 stat를 true로 바꿈 (hasMounted props를 바꾸고 다시 렌더)
componentDidMount() {
this.setState({ hasMounted:true });
}
}
// 새로운 컴포넌트를 디버깅할 경우 쉽게 알아보기 위해 displayName 설정
NewComponent.displayName = `withHasMounted(${Component.name})`;
// 최종적으로 새 컴포넌트를 리턴
return NewComponent;
}
import { useState } from "react"
export default function useHasMounted() {
const [hasMounted] = useState(false);
useEffect( () => {} // 상태를 변경하는 로직
setHasMounted(true);
}, []); // dependency
return hasMounted()
}
import Example1 from "./components/Example1";
import Example2 from "./components/Example2";
import Example3 from "./components/Example3";
import Example4 from "./components/Example4";
import Example5 from "./components/Example5";
import useWindowWith "./hooks/useWindowWith";
import withHasMounted from "./hocs/withHasMounted"; // 추가
import useHasMounted from "./hooks/useHasMounted"; // 추가
function App( {hasMounted} ) {
// useHasMounted를 통해서 state값을 가져옴
const width = useWindowWith();
const hasMountedFromHooks = useHasMounted();
console.log(hasMounted); // 최초 false, 다시 렌더되면서 true
console.log(hasMounted, hasMountedFromHooks); // false, false true, true
return (
<div className="App">
<header className="App-header">
<Example1 />
<Example2 />
<Example3 />
<Example4 />
<Example5 />
{width}
</header>
</div>
);
}
export default withHasMounted(App);
// HOC를 사용해 App은 인자로 하고 hasMounted props를 가지게 되는 새로운 컴포넌트로 출력
useReducer
, useCallback
, useMemo
, useRef
useReducer
reducer
: 즉시 실행 순수 함수import React from "react";
// reducer -> state를 변경하는 로직이 담겨있는 함수
// 인자로 받는 state는 이전 상태 값
const reducer = (state, action) => {
if (action.type === 'PLUS'){
// dispatch에서 보내오는 값을 통해서 로직 결정
return {
count: state.count + 1,
};
}
return state; // 변경없는 state
}
// dispatch -> action 객체를 넣어서 실행
// action -> 객체이고 필수 프로퍼티로 type을 가짐
export default function Example6() {
const [state, dispatch ] = useReducer(reducer, {count: 0});
return (
<div>
<p>You clocked {count} times</p>
<button onClick={click}>Click me</button>
</div>
);
function click() {
// 요소에서 event를 받으면, dispatch 함수에 특정 type 값을 가지는 action 객체를 넣어 실행
dispatch({type: "PLUS" });
}
}
import Example6 from "./components/Example6"; // useReducer 추가
import useWindowWith "./hooks/useWindowWith";
import withHasMounted from "./hocs/withHasMounted";
import useHasMounted from "./hooks/useHasMounted";
function App( {hasMounted} ) {
const width = useWindowWith();
const hasMountedFromHooks = useHasMounted();
console.log(hasMounted);
console.log(hasMounted, hasMountedFromHooks);
return (
<div className="App">
<header className="App-header">
<Example 6 /> // Click 시 count
{width}
</header>
</div>
);
}
useMemo
useCallback
useCallback(callback, dependency)
import { useState, useMemo, useCallback } React from "react";
// sum 함수 persons 배열을 넣으면 , 해당 사람들의 age를 합산해서 return 함
function sum(persons) {
console.log("sum...");
return persons.map(person => person.age).reduce((l, r) => l + r, 0);
}
export default function Example7() {
const [value, setValue] = useState('');
const [persons] = useState([
{name: 'Mark', age: 39},
{name: 'Hanna', age: 28},
]);
// const count = sum(persons);
// persons가 변했을 때만 count를 실행함
const count = useMemo( () => {
return sum(persons);
}, [persons]); // dependency
// 이전의 최초 state 값만 유지하고 있음(value가 update되어도 여기선 update가 안됨)
const click = useCallback( () => {
console.log(value);
}, []); // 최초에만 생성되도록 제한
const click = useCallback( () => {
console.log(value);
}, [value]); // state의 최신 값을 활용해 callback 실행
return(
<div>
<input value={} onChange={} />
<p>Hello</p>
<button onClick={click}>click</button>
</div>
);
fucntion change(e) {
setValue(e.target.value);
}
}
import Example6 from "./components/Example6";
import Example7 from "./components/Example7"; // 추가
import useWindowWith "./hooks/useWindowWith";
import withHasMounted from "./hocs/withHasMounted";
import useHasMounted from "./hooks/useHasMounted";
function App( {hasMounted} ) {
const width = useWindowWith();
const hasMountedFromHooks = useHasMounted();
console.log(hasMounted);
console.log(hasMounted, hasMountedFromHooks);
return (
<div className="App">
<header className="App-header">
<Example 7 /> // input
{width}
</header>
</div>
);
}
useRef
import { createRef, useRef, useState } React from "react";
export default function Example8() {
const [value, setValue] = useState('');
const input1Ref = createRef(); // 렌더되면 reference를 항상 다시 생성해서 렌더될 때 넣어줌
const input2Ref = useRef(); // 렌더되어도 항상 reference를 계속 유지
console.log(input1Ref.current, input2Ref.current);
return(
<div>
<input value={} onChange={} /> // Controlled Component
<input ref={input1Ref} /> // Uncontrolled Component1
<input ref={input2Ref} /> // Uncontrolled Component2
</div>
);
fucntion change(e) {
setValue(e.target.value);
}
}
import Example6 from "./components/Example6";
import Example7 from "./components/Example7";
import Example8 from "./components/Example8"; // 추가
import useWindowWith "./hooks/useWindowWith";
import withHasMounted from "./hocs/withHasMounted";
import useHasMounted from "./hooks/useHasMounted";
function App( {hasMounted} ) {
const width = useWindowWith();
const hasMountedFromHooks = useHasMounted();
console.log(hasMounted);
console.log(hasMounted, hasMountedFromHooks);
return (
<div className="App">
<header className="App-header">
<Example 8 /> // input
{width}
</header>
</div>
);
}
https://ko.reactjs.org/docs/hooks-custom.html
https://goforit.tistory.com/131
https://react.vlpt.us/basic/21-custom-hook.html
https://velog.io/@kysung95/%EC%A7%A4%EB%A7%89%EA%B8%80-Custom-Hooks%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90
https://ko-de-dev-green.tistory.com/68
https://ko-de-dev-green.tistory.com/71
이어서.