-
Notifications
You must be signed in to change notification settings - Fork 0
리액트 디자인 패턴 #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Container/Presentational 패턴
컴포넌트를 두개로 나눔
예시이미지 리스트 렌더링 //DogImages.jsx(Presentational)
export default function DogImages({ dogs }) {
return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
} prop을 통해 데이터 받고 그대로 화면에 그리는 역할 UI변경을 위한 상태말고는 다른 상태를 가지지 않는다. prop으로 받은 데이터는 Presentational Component에 의해 수정되지 않는다. // Container
export default class DogImagesContainer extends React.Component {
constructor() {
super();
this.state = {
dogs: []
};
}
componentDidMount() {
fetch("https://dog.ceo/api/breed/labrador/images/random/6")
.then(res => res.json())
.then(({ message }) => this.setState({ dogs: message }));
}
render() {
return <DogImages dogs={this.state.dogs} />;
}
} Presentational Component에 데이터를 전달하는게 목적 Presentational이외에는 아무것도 렌더링 하지 않는다.(style도 존재X) API를 불러와 상태에 저장하고 Presentational에 prop으로 전달 여기까지 클래스 기반 해당 패턴은 Hook이 나오기전 주로 사용하던 방법으로 Hook으로 대체 가능하다.(예전엔(클래스) 이렇게 비즈니스 로직을 분리하곤 했다 정도) Custom Hook을 사용하면 //Custom Hook
export default function useDogImages() {
const [dogs, setDogs] = useState([])
useEffect(() => {
fetch('https://dog.ceo/api/breed/labrador/images/random/6')
.then(res => res.json())
.then(({ message }) => setDogs(message))
}, [])
return dogs
}
//렌더링 컴포넌트
export default function DogImages() {
const dogs = useDogImages();
return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
} 쉽게 비즈니스 로직을 분리할 수 있다. 불필요한 Container Component작성을 줄일 수 있다. 장점자연스럽게 관심사 분리 Presentational Component를 여러곳에서 재사용 가능 Presentational Component를 코드베이스에 대한 이해가 깊지 않은 개발자도 쉽게 수정 가능하고, 재사용된 전체를 한번에 수정가능 단점Hook의 등장으로 해당 패턴 없이도 쉽게 구현가능해짐 Hook을 사용하더라도 사용가능하지만 오버엔지니어링이 될 가능성이 높다.
주로 Hook을 쓰는 지금 자연스럽게 적용되는 디자인 패턴이기도 하고 현재는 거의 필요하지 않으므로 한가지만 더 간단하게 알아보자 HOC패턴(Higher Order Component)앱 전반적으로 재사용 가능한 로직을 prop으로 컴포넌트에게 제공한다 여러 컴포넌트에서 같은 로직을 사용해야하는 경우(스타일을 적용하거나, 권한을 요청하거나 등등) 고차 컴포넌트란?다른 컴포넌트를 받는 컴포넌트 예제여러 컴포넌트에 동일한 스타일을 적용하고 싶다. HOC가 style 객체를 직접 만들어 컴포넌트에 전달 function withStyles(Component) {
return props => {
const style = { padding: '0.2rem', margin: '1rem' }
return <Component style={style} {...props} />
}
}
const Button = () = <button>Click me!</button>
const Text = () => <p>Hello World!</p>
const StyledButton = withStyles(Button)
const StyledText = withStyles(Text) 이전 Container/Presentational 패턴의 예제에서 이미지를 가져오는 동안은 로딩표시를 하고 싶다면?export default function DogImages() {
const dogs = useDogImages();
return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
} 로딩을 표시하는 고차 컴포넌트(HOC) export default function withLoader(Element, url) {
return (props) => {
const [data, setData] = useState(null);
useEffect(() => {
async function getData() {
const res = await fetch(url);
const data = await res.json();
setData(data);
}
getData();
}, []);
if (!data) {
return <div>Loading...</div>;
}
return <Element {...props} data={data} />;
};
} fetch가 완료되기 전에는 로딩중임을 보여주는 컴포넌트 function DogImages(props) {
return props.data.message.map((dog, index) => (
<img src={dog} alt="Dog" key={index} />
));
}
export default withLoader(
DogImages,
"https://dog.ceo/api/breed/labrador/images/random/6"
); 고차 컴포넌트 조합호버링 추가하고 싶을때 export default function withHover(Element) {
return props => {
const [hovering, setHover] = useState(false);
return (
<Element
{...props}
hovering={hovering}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
/>
);
};
} 두번 감싸기 export default withHover(
withLoader(DogImages, "https://dog.ceo/api/breed/labrador/images/random/6")
); Hook고차 컴포넌트 대신 호버링 Hook으로 구현(커스텀 훅) export default function useHover() {
const [hovering, setHover] = useState(false);
const ref = useRef(null);
const handleMouseOver = () => setHover(true);
const handleMouseOut = () => setHover(false);
useEffect(() => {
const node = ref.current;
if (node) {
node.addEventListener("mouseover", handleMouseOver);
node.addEventListener("mouseout", handleMouseOut);
return () => {
node.removeEventListener("mouseover", handleMouseOver);
node.removeEventListener("mouseout", handleMouseOut);
};
}
}, [ref.current]);
return [ref, hovering];
} 간단하게 커스텀 훅 호출해 호버링 구현 function DogImages(props) {
const [hoverRef, hovering] = useHover();
return (
<div ref={hoverRef} {...props}>
{hovering && <div id="hover">Hovering!</div>}
<div id="list">
{props.data.message.map((dog, index) => (
<img src={dog} alt="Dog" key={index} />
))}
</div>
</div>
);
} but, 일반적으로 Hook은 HOC를 완전히 대체하지 못한다.
HOC사용 사례전반적으로 동일하고 커스텀 불가능한 동작이 여러 컴포넌트에 필요할때 컴포넌트가 어떤 로직 추가 없이 단독으로 실행되어야 할 경우 React의 fallback…? Hook사용사례공통 기능이 각 컴포넌트에서 쓰이기 전에 커스터마이징 되어야 하는 경우 공통 기능이 앱 전반적으로 쓰이는 것이 아닌 하나나 혹은 몇개의 컴포넌트에서 요구되는 경우 해당 기능이 기능을 쓰는 컴포넌트에게 여러 프로퍼티를 전달해야 하는 경우 단점여러 고차 컴포넌트를 조합할 경우 모든 prop이 최종적으로 병합되므로 어떤 고차 컴포넌트가 어떤 prop와 관련있는지 파악이 어렵다. 고차컴포넌트를 사용할때 감싸는 순서가 중요해지는 경우 버그를 만들어내기 쉽다. 두가지 패턴을 살펴본 결과 Hook이란게 매우 많은 역할을 하고 있다 함수형 컴포넌트에서 상태를 사용할 수 있도록 하는 역할만 하는줄 알았지만 디자인 패턴 관점에서도 훌륭한 이점을 가지고 있다는걸 깨달았다.(HOC를 완전히 대체할수는 없지만) |
compound pattern하나의 큰 컴포넌트가 가지는 로직와 디자인이 결합도가 높아 변경에 유연하지 않은 문제를 compound pattern으로 여러 컴포넌트를 복합적으로 구성하는 방식으로 해결하는 패턴예제 codesandbox |
No description provided.
The text was updated successfully, but these errors were encountered: