dry 란: Don't repeat yourself을 의미 합니다. 중복을 피하기 위해서 함수로 해당 로직을 만들거나, 컴포넌트로 공통으로 사용될 UI를 만들기도 합니다.
react에서 사용할 수 있는 custom hook 과 HOC(higher order component)를 알아보겠습니다.
우리는 함수 컴포넌트에서 사용하는 hook에 대해 배웠습니다. 하지만 기본적으로 제공하는 hook만으로 우리가 원하는 모든 것을 해결할 수는 없습니다. hook과 hook을 조합해야하기도 하고 다른 로직이 많이 들어가기도 합니다. 이러한 경우 우리만의 hook을 만드는 방법이 "custom hook" 입니다.
이는 여러곳에서 사용하는 특정 hook을 한번에 관리할 수 있고, 이로 인해 해당 컴포넌트에서의 코드가 간결해진다는 장점이 있습니다.
https://usehooks.com/ 에서 많은 예시를 볼 수 있습니다.
https://usehooks.com/uselocalstorage 재밌는 예~시
demo 보기 전에 어떻게 동작할 지 추측해보기.
import createDrawing from "./createDrawing";
export default function App() {
const [drawing, saveDrawing] = useLocalStorage("drawing", null);
const ref = React.useRef(null);
React.useEffect(() => {
createDrawing(ref.current, drawing, saveDrawing);
}, [drawing, saveDrawing]);
return (
<section>
<header>
<h1>useLocalStorage</h1>
<button className="link" onClick={() => window.location.reload()}>
Reload Window
</button>
<button
className="link"
onClick={() => {
window.localStorage.clear();
window.location.reload();
}}
>
Clear Local Storage
</button>
</header>
<figure>
<canvas ref={ref} width={800} height={800} />
<figcaption>(draw something)</figcaption>
</figure>
</section>
);
}
import { useState } from 'react';
function useFormInput(initialValue) {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
const reset = () => {
setValue(initialValue);
};
return {
value,
onChange: handleChange,
reset,
};
}
import React from 'react';
import { useFormInput } from './useFormInput';
function FormComponent() {
const name = useFormInput('');
const handleSubmit = (e) => {
e.preventDefault();
alert(`Submitting Name: ${name.value}`);
name.reset();
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" {...name} />
</label>
<button type="submit">Submit</button>
</form>
);
}
고차 컴포넌트 (HOC)란 컴포넌트 자체의 로직을 다시 재사용 하기 위한 방법입니다.
고차함수와 비슷한 의미라고 생각하시면 됩니다.
대표적인 예시는 React.memo가 있겠습니다.
고차 구성 요소 정의 (주의 : 고차 컴포넌트는 with로 시작해야합니다)
import React from 'react';
// HOC는 컴포넌트(WrappedComponent)를 받아서 새로운 컴포넌트를 return 해줍니다.
function withLoading(Component) {
return class WithLoading extends React.Component {
render() {
const { isLoading, ...props } = this.props;
if (isLoading) {
return <div>Loading...</div>;
}
return <Component {...props} />;
}
};
}
고차 구성요소 사용
HOC로 wrapping 해서 export 하기
import React from 'react';
import { withLoading } from './withLoading';
const MyComponent = ({ data }) => (
<div>{data}</div>
);
export default withLoading(MyComponent);
wrapping된 컴포넌트라서 isLoading이라는 props를 사용할 수 있습니다.
const example = () => (
<MyComponent isLoading={true} />
)
모두 리액트에서 어떤 로직을 공통화해서 관리할 수 있도록 해줍니다.
이를 통해 가독성을 좋게 해줄 수 있습니다. (잘 썼을 경우)
hook들을 사용해서 로직을 분리 할 수 있다면, 최대한 custom hook으로 처리하자
HOC에 비해 쉽게 이해할 수 있기 때문입니다.
함수형 컴포넌트의 반환값, 즉 랜더링의 결과물에 영향을 미치는 공통로직이라면 고차 컴포넌트로 간결하게 정리할 수 있다.
but, 주의해야할 점은 고차 컴포넌트에 또 고차 컴포넌트를 추가하는 방식의 구현은 가독성을 떨어뜨리고 해당 로직을 추측하기 어렵게 만듭니다.