[Ch7] Higher-order components in modern world
React์ ๋ค์ํ ์กฐํฉ ๊ธฐ์ ์ค ํ๋์ธ ๊ณ ์ฐจ ์ปดํฌ๋ํธ(Higher-Order Component, HOC)์ ๋ํด ์์๋ณด์.
๊ณ ์ฐจ ์ปดํฌ๋ํธ๋?
React ๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด, HOC๋ ์ปดํฌ๋ํธ ๋ก์ง์ ์ฌ์ฌ์ฉํ๋ ๊ธฐ์ ์ด๋ค. ์ฝ๊ฒ ๋งํด, ์ปดํฌ๋ํธ๋ฅผ ์ธ์๋ก ๋ฐ์ ๋ด๋ถ์์ ํน์ ๋ก์ง์ ์คํํ ํ ์๋ก์ด ์ปดํฌ๋ํธ๋ฅผ ๋ฐํํ๋ ํจ์์ด๋ค.
๊ธฐ๋ณธ์ ์ธ HOC ํจํด
const withSomeLogic = (Component) => {
return (props) => <Component {...props} />;
};
const Button = ({ onClick }) => (
<button onClick={onClick}>Button</button>
);
const ButtonWithSomeLogic = withSomeLogic(Button);
HOC๋ ๊ธฐ์กด ์ปดํฌ๋ํธ์ ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์์ผ๋ฉฐ, ๊ตฌํ ๋ฐฉ์์ด ๊ฐ๋จํ๋ฉด์๋ ๊ฐ๋ ฅํ ํ์ฅ์ฑ์ ์ ๊ณตํ๋ค.
๊ณ ์ฐจ ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ props ์ฃผ์
HOC๋ ์ปดํฌ๋ํธ์ props๋ฅผ ์ฃผ์ ํ๋ ์ฉ๋๋ก ์์ฃผ ์ฌ์ฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์๋์ ์ฝ๋์์๋ ํ ๋ง ์ ๋ณด๋ฅผ ๋ชจ๋ ์ปดํฌ๋ํธ์ ์๋์ผ๋ก ์ ๋ฌํ๋ค.
const withTheme = (Component) => {
const theme = isDark ? "dark" : "light";
return (props) => <Component {...props} theme={theme} />;
};
const Button = ({ theme }) => (
<button className={theme}>Button</button>
);
const ButtonWithTheme = withTheme(Button);
์ด ํจํด์ ํ์ฉํ๋ฉด, ๊ฐ ์ปดํฌ๋ํธ์์ theme์ ์ง์ ๊ด๋ฆฌํ ํ์ ์์ด, HOC๋ฅผ ์ ์ฉํ๋ ๊ฒ๋ง์ผ๋ก ์๋์ผ๋ก ํ ๋ง ์ ๋ณด๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๊ณ ์ฐจ ์ปดํฌ๋ํธ ๋ฐฐ๊ฒฝ
HOC๋ Redux์ connect ํจ์, React Router์ withRouter ๋ฑ์์ ๋๋ฆฌ ์ฌ์ฉ๋์๋ค.
ํ์ง๋ง ํ ๋ฑ์ฅ ์ดํ HOC์ ์ฌ์ฉ ๋น๋๊ฐ ์ค์ด๋ค์์ผ๋ฉฐ, ๋ง์ ๊ฒฝ์ฐ useTheme ๊ฐ์ ํ ์ ํ์ฉํ์ฌ HOC๋ฅผ ๋์ฒดํ๊ณ ์๋ค.
const Button = () => {
const { theme } = useTheme();
return <button className={theme}>Button</button>;
};
HOC๋ ํ๋ ์ฝ๋์ ๋ชจ๋ํ์ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๋ ์ค์ํ ํจํด์ด์์ง๋ง, ์ง๊ธ์ ํ ์ ์ด์ฉํ ํด๊ฒฐ ๋ฐฉ์์ ์ ํธํ๋ค.
๊ณ ์ฐจ ์ปดํฌ๋ํธ ์ฌ์ฉ ์ฌ๋ก
โ HOC๋ฅผ ํ์ฉํ๋ฉด ์ฝ๋ฐฑ ํจ์ ์คํ ์ ๊ณตํต์ ์ธ ๋ก์ง(์: ๋ก๊น )์ ์ถ๊ฐํ ์ ์๋ค.
export const withLoggingOnClick = (Component) => {
return (props) => {
const onClick = () => {
console.log("Log on click something");
if (props.onClick) props.onClick();
};
return <Component {...props} onClick={onClick} />;
};
};
export const ButtonWithLoggingOnClick = withLoggingOnClick(Button);
โ ์ปดํฌ๋ํธ ๋ง์ดํธ ์ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ HOC๋ฅผ ๊ตฌํํ ์๋ ์๋ค.
export const withLoggingOnMount = (Component) => {
return (props) => {
useEffect(() => {
console.log("log on mount");
}, []);
return <Component {...props} />;
};
};
HOC๋ฅผ ํ์ฉํ๋ฉด, ๋ชจ๋ ์ปดํฌ๋ํธ์์ ์๋ช ์ฃผ๊ธฐ ๋ก์ง์ ์ค๋ณต ์์ฑํ ํ์ ์์ด, ํ ๊ณณ์์ ์ฒ๋ฆฌํ ์ ์๋ค.
โ ๊ณ ์ฐจ ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ์ฌ ํน์ DOM ์ด๋ฒคํธ๋ฅผ ๊ฐ๋ก์ฑ ์๋ ์๋ค.
export const withSuppressKeyPress = (Component) => {
return (props) => {
const onKeyPress = (event) => event.stopPropagation();
return (
<div onKeyPress={onKeyPress}>
<Component {...props} />
</div>
);
};
};
const ModalWithSuppressedKeyPress = withSuppressKeyPress(Modal);
๊ณ ์ฐจ ์ปดํฌ๋ํธ์ ์ฅ๋จ์
โ ์ฅ์
- ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์ผ ์ ์๋ค.
- props๋ฅผ ์กฐ์ํ์ฌ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ์ฐํ๊ฒ ์ถ๊ฐํ ์ ์๋ค.
- ์๋ช ์ฃผ๊ธฐ ์ด๋ฒคํธ๋ ํน์ ๋์์ ์ค์์์ ๊ด๋ฆฌํ ์ ์๋ค.
โ ๋จ์
- ์ปดํฌ๋ํธ ๊ตฌ์กฐ๊ฐ ๊น์ด์ง์๋ก ๋๋ฒ๊น ์ด ์ด๋ ค์ธ ์ ์๋ค.
- ๋ถํ์ํ ์ค์ฒฉ์ด ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
- React์ ์ต์ ํจํด(ํ )๊ณผ ๋น๊ตํ์ ๋, ์๋์ ์ผ๋ก ์ฝ๋ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง ์ ์๋ค.
์ ๋ฆฌ
- ๊ณ ์ฐจ ์ปดํฌ๋ํธ๋ ๋จ์ํ ํจ์๋ก, ๊ธฐ์กด ์ปดํฌ๋ํธ์ ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ์ญํ ์ ํ๋ค.
- props ์ฃผ์ , ์ฝ๋ฐฑ ํจ์ ํฅ์, ์๋ช ์ฃผ๊ธฐ ์ด๋ฒคํธ ๊ด๋ฆฌ, DOM ์ด๋ฒคํธ ๊ฐ๋ก์ฑ๊ธฐ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๋ค.
- ํ ๋ฑ์ฅ ์ดํ ์ฌ์ฉ ๋น๋๊ฐ ์ค์์ง๋ง, ํน์ ์ํฉ์์๋ ์ ์ฉํ๊ฒ ํ์ฉ ๊ฐ๋ฅํ๋ค.
- ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ณ , ํน์ ๊ธฐ๋ฅ์ ๋ถ๋ฆฌํ ๋ ํจ๊ณผ์ ์ด์ง๋ง, ํ์ ์ด์์ผ๋ก ์ค์ฒฉ๋๋ฉด ์ฝ๋ ๊ฐ๋ ์ฑ์ด ์ ํ๋ ์ ์๋ค.