React- Hooks #2

정지훈·2021년 3월 12일
1

Custom Hooks (내가 만든 훅)

나만의 훅을 만들어 보자.
파일 하나를 생성하자. useWindowWidth.js파일을 만들자.

  • useWindowWidth.js
export default function useWindowWidth() {
  const [width] = useState(window.innderWidth);
  
  return width;
}
  • Example5.jsx
import { useEffect, useState } from "react";
import useWindowWidth from "../hooks/useWindowWidth";

// state = { count: 0 }
export default function Example5() {
	const [count, setCount] = useState(0);
	const [name, setName] = useState("Mark");

	const width = useWindowWidth();

	// 2개의 인자를 받는다. 하나는 함수 이 아이는 시점이 없다.
	useEffect(() => {
		console.log("componentDidMount & componentDidUpdate");
		return () => {
			console.log(`clean up`);
		};
	}); // 시점을 지정하지 않으면 무조건 랜더 된 직후를 의미 한다.

	// 2번째 인자로 빈 배열을 넣으면 componentDidMount만 실행한다. 즉 함수는 실행되는 아이(한일?)를 의미하고 2번째 인자인 배열은 시점을 이야기 하는 것이다.(언제의 뜻?)
	useEffect(() => {
		console.log("componentDidMount");

		return () => {
			console.log("componentWillUnmount");
		}; // 함수를 리턴을 한다. 함수를 반환하면 해당 함수는 다음 랜더를 하기 전에 실행한다.
	}, []); // 시점이 빈 배열이면 최초에 렌더 된 직후를 의미(의존성?)

	useEffect(() => {
		console.log("[count]", count);

		return () => {
			console.log("[count] - cleanup", count);
		};
	}, [count]);

	return (
		<div>
			<h2>
				{name} - {width}
			</h2>
			<p>You clicked {state.count} times</p>
			<button onClick={this.click}>Click me</button>
		</div>
	);
	function click() {
		// setState => 두 번째 배열 요소
		// setState({ count: state.count + 1 }); // 이렇게 도 사용할 수 있고 함수로도 사용가능하다.
		setState(count + 1);
	}
}

이렇게 하면 name 옆에 현재 가로값이 나올 것이다. 위에서 하고 싶었던 거는 가로값을 줄었다 늘렸다 하면 가로값이 화면에서 나오게 하는 훅을 만들고 싶은 거다.

그래서 다른 컴포넌트를 만들어서 해보자.

  • Example6.jsx
import { useState } from "react";
import useWindowWidth from "../hooks/useWindowWidth";

// state = { count: 0 }
export default function Example5() {
	const [count, setCount] = useState(0);
	const [name, setName] = useState("Mark");

	const width = useWindowWidth();

	return (
		<div>
			<h2>
				{name} - {width}
			</h2>
			<p>You clicked {state.count} times</p>
			<button onClick={this.click}>Click me</button>
		</div>
	);
	function click() {
		// setState => 두 번째 배열 요소
		// setState({ count: state.count + 1 }); // 이렇게 도 사용할 수 있고 함수로도 사용가능하다.
		setState(count + 1);
	}
}

커스텀 훅이 왜 나왔나? 상태에 대한 로직을 재사용하기 어렵다. 재사용을 위해서 따로 때어내는 행위를 하는 것이다. useWindowWidth는 이렇게 사용할 수 있다.

import { useEffect, useState } from "react";
import useWindowWidth from "../hooks/useWindowWidth";

// state = { count: 0 }
export default function Example5() {
	const [count, setCount] = useState(0);
	const [name, setName] = useState("Mark");

	const [width, setWidth] = useState(window.innderWidth);

	// 우리가 화면 크기를 줄이거나 늘릴때 알아차리는건 window객체이다. 즉 render 직후에 이벤트를 달아줘야한다.

	useEffect(() => {
		const resize = () => {
			setWidth(window.innerWidth);
		};
		window.addEventListener("resize", resize); // reference를 넣어야하기 때문에 함수를 만들어서 참조값을 갖고 있는 식별자를 넣는다.
		return () => {
			window.removeEventListener("resize", resize);
		};
	}, []);

	return (
		<div>
			<h2>
				{name} - {width}
			</h2>
			<p>You clicked {state.count} times</p>
			<button onClick={this.click}>Click me</button>
		</div>
	);
	function click() {
		// setState => 두 번째 배열 요소
		// setState({ count: state.count + 1 }); // 이렇게 도 사용할 수 있고 함수로도 사용가능하다.
		setState(count + 1);
	}
}

이렇게 만들 수 있다.

const [width, setWidth] = useState(window.innderWidth);

// 우리가 화면 크기를 줄이거나 늘릴때 알아차리는건 window객체이다. 즉 render 직후에 이벤트를 달아줘야한다.

useEffect(() => {
	const resize = () => {
		setWidth(window.innerWidth);
	};
	window.addEventListener("resize", resize); // reference를 넣어야하기 때문에 함수를 만들어서 참조값을 갖고 있는 식별자를 넣는다.
	return () => {
		window.removeEventListener("resize", resize);
	};
}, []);

이 부분을 우리가 만든 useWindowWidth.js에 그대로 합치자

  • useWindowWidth.js
import { useEffect, useState } from "react";
export default function useWindowWidth() {
	const [width, setWidth] = useState(window.innderWidth);

	// 우리가 화면 크기를 줄이거나 늘릴때 알아차리는건 window객체이다. 즉 render 직후에 이벤트를 달아줘야한다.

	useEffect(() => {
		const resize = () => {
			setWidth(window.innerWidth);
		};
		window.addEventListener("resize", resize); // reference를 넣어야하기 때문에 함수를 만들어서 참조값을 갖고 있는 식별자를 넣는다.
		return () => {
			window.removeEventListener("resize", ㄱㄷ냨ㄷ);
		};
	}, []);
	return width;
}

이렇게 옮기고 Example6.jsx에서

import { useState } from "react";
import useWindowWidth from "../hooks/useWindowWidth";

// state = { count: 0 }
export default function Example5() {
	const [count, setCount] = useState(0);
	const [name, setName] = useState("Mark");

	const width = useWindowWidth();

	return (
		<div>
			<h2>
				{name} - {width}
			</h2>
			<p>You clicked {state.count} times</p>
			<button onClick={this.click}>Click me</button>
		</div>
	);
	function click() {
		// setState => 두 번째 배열 요소
		// setState({ count: state.count + 1 }); // 이렇게 도 사용할 수 있고 함수로도 사용가능하다.
		setState(count + 1);
	}
}

이렇게 간단한 윈도우의 가로값을 가져오는 훅을 만들었다.
훅은 다른 훅에서 사용하거나 함수 컴포넌트에서만 사용할 수 있다.
custom훅은 useState와 useEffect로 이루어 진다.

하나 더 만들어 보자.
hoc로 만든 withHasMounted와 훅으로 만든 useHasMounted를 만들어서 비교해 보자.

처음에는 HOC를 만들자.
withHasMounted.js를 만들고 useHasMounted.js도 하나 파일을 만들자.

import React from "react";
export default function withHasMounted(Component) {
	class C extends React.Component {
		state = {
			hasMounted: false,
		};

		render() {
			return <Component {...this.props} hasMounted={this.state.hasMounted} />;
		}
		componentDidMount() {
			this.setState({ hasMounted: true });
		}
	}

	return C;
}

Example6.jsx에서 써보자.

import { useState } from "react";
import useWindowWidth from "../hooks/useWindowWidth";
import withHasMounted from "../hocks/withHasMounted";

function Example6(props) {
	const [count, setCount] = useState(0);
	const [name, setName] = useState("Mark");

	const width = useWindowWidth();

	return (
		<div>
			<h2>
				{name} - {width} - {props.hasMounted.toString()}
			</h2>
			<p>You clicked {state.count} times</p>
			<button onClick={this.click}>Click me</button>
		</div>
	);
	function click() {
		setState(count + 1);
	}
}
// state = { count: 0 }
export default withHasMounted(Example6);

이렇게 만든 아이를 훅으로 만들자.

  • useHasMounted.js
export default function useHasMounted() {
	const [hasMounted, setHasMounted] = useState(false);

	useEffect(() => {
		setHasMounted(true);
	}, []);
	return hasMounted;
}

이렇게 하면 훅이 만들어 진다.

  • Example6.jsx
import { useState } from "react";
import useWindowWidth from "../hooks/useWindowWidth";
import withHasMounted from "../hocks/withHasMounted";

function Example6(props) {
	const [count, setCount] = useState(0);
	const [name, setName] = useState("Mark");

	const width = useWindowWidth();
	const hasMounted = useHasMounted();

	return (
		<div>
			<h2>
				{name} - {width} - {props.hasMounted.toString()}
			</h2>
			<p>You clicked {state.count} times</p>
			<button onClick={this.click}>Click me</button>
		</div>
	);
	function click() {
		setState(count + 1);
	}
}
// state = { count: 0 }
export default withHasMounted(Example6);

이렇게 보면 차이점이 드러난다. hoc를 사용할 때마다 컴포넌트를 감싸서 하나의 컴포넌트를 만든다. 즉 많이 사용하면 컴포넌트가 계속 랩핑된다. 그래서 새로운 용어가 나왔다. 예전에 콜백 핼이라고 불렸던 것 처럼 랩핑 핼이 나왔다.

이와 다르게 훅은 다르게 랩핑을 하는 일이 없이 하나의 state를 사용하는데 로직만 따로 분리를 해서 간편하게 useHasMounted에 옮겨 놓았다. 그래야 HOC를 떠나서 hooks로 오는 것이다.

왜 hoc보다 더 hooks를 더 선호하냐?
HOC를 사용하면 변화되는 데이터를 프롭스로 관리하는데 그 프롭스를 위에서 계속 컴포넌트를 랩핑하는 과정이 생긴다.
그 과정에서의 복잡도도 증가하고 불필요한 랩핑으로 인해서 디버깅도 힘들다.

0개의 댓글