Search
Duplicate
📒

합성

수업
React 완벽 가이드
주제
5 more properties
참고

리액트 설계

NOTE
설계란 코드를 배치하는 행위이다. 동일한 기능이더라도 코드의 위치가 달라질 수 있다.
설계란 코드를 배치하는 행위이다. 동일한 기능이더라도 코드의 위치가 달라질 수 있 다. 코드를 어디에, 어떻게 배치하는것이 바로 설계의 영역이다.
1개의 컴포넌트안에 5~6개의 역할을 하는 코드가 있다.
1개의 역할말은 가진 5~6개의 컴포넌트가 존재할 수 있다.
좋은 설계는 2 가지 요구사항을 만족해야 한다.
1.
기능을 온전히 수행해야 한다.
2.
유지보수가 쉬워야한다.

목차

NOTE
의존성 역전원칙을 적용한 이후에 변경의 전파가 얼마나 효율적으로 차단되는가? 코드의 수정이 생길때, 변경사항이 얼마나 많이 줄어들었는가?
변경의 전파를 줄여 유지보수를 용이하게 만들고자 하는 객체지향의 원리를, 5가지 지침으로 정리해놓은 것이 바로 SOLID 원칙이다.
의존성 역전 원칙은 기본적으로 의존성의 방향에 의문을 가지도록 하는 원칙이다.
의존성의 방향이 어디로 향하고 있는가?
쉽게 변하지 않는 추상적인 대상에 의존하고 있는가?
맥락에 따라 쉽게 변할 수 있는 구체적인 대상이 의존하고 있는가?
쉽게 변하지 않는 대상에 의존하도록 만들어야 한다.
왜 변하지 않는 대상에 의존해야 하는가?
의존성은 기본적으로 ‘변경’의 전파와 관련이 있다.
컴포넌트가 자주 변경되는 대상에 의존중이라면, 그 만큼 코드의 변경이 자주 발생하게 된다.
하지만 자주 변하지 않는 대상에 의존한다면, 의존했다는 이유로 변경할 이유가 적어진다.
의존성 역전의 원칙은 구체적인 구현보다, 안정적이고 추상적인 인터페이스에 의존하도록 강제하는 것이다.
추상적인 인터페이스는 잘 변경되지 않으며, 상위정책에 해당한다.

목차

합성 컴포넌트(Compund Component)

NOTE
합성 컴포넌트 패턴은 여러 하위 컴포넌트들을 부모 컴포넌트로 묶어서 관리합니다.
// 원본 const Usage = () => { return <MediumClap /> } // 합성 컴포넌트(내부 구조가 보인다.) <MediumClap onClap={() => console.log("박수!")}> <ClapIcon /> <ClapCount /> <Total /> </MediumClap>
TypeScript
복사
interface MediumClapProps { children: ReactNode; onClap: () => void }; export const MediumClapContext = createContext<{count: number; increment: () => void} | undefined>(undefined); const MediumClap = ({children, onClap}: MediumClapProps) => { const [count, setCount] = useState(0); const increment = () =>{ onClap(); setCount(count + 1); } return ( <MediumClapContext.Provider value={{count, increment}}> <Box>{children}</Box> </MediumClapContext.Provider> ); }; export default MediumClap
TypeScript
복사
const ClapCount: React.FC = () => { const context = useContext(MediumClapContext); return <Text fontSize="2xl">{context?.count}</Text>; }; export default ClapCount;
TypeScript
복사
const ClapIcon = () => { const { increment } = useContext(MediumClapContext) || { increment: () => {} }; return( <Button size="lg" colorScheme="teal" onClick={increment}> 👏 </Button> ) }; export default ClapIcon;
TypeScript
복사
export const Total = () => { const context = useContext(MediumClapContext); const total = 100 + (context?.count ?? 0); return <Text fontSize="md">Total claps: {total}</Text>; };
TypeScript
복사

장점

1.
매우 Simple한 props
2.
props drilling을 방지
3.
유연한 UI구조
4.
관심사 분리(핵심은 부모 컴포넌트, 자식에 해당하는 컴포넌트들은 각자 적용)

UI 레이아웃 고정하기

NOTE
현재는 {children}으로 컴포넌트가 들어오면서, 사용부에서 순서대로 나열되는대로 화면에 그려집니다. 그러나 분명 특정 상황에서는 부모 컴포넌트가 미리 디자인 프레임을 가지고 있으며, 그 프레임에 자식을 넣고 싶을 수도 있습니다.
특정 위치에 렌더링되도록 하고 싶은 컴포넌트의 자식 타입을 지정하고, 필터링 하는 방법으로 레이아웃에 맞춰 컴포넌트를 넣을 수 있습니다.
interface ClapContextType { count: number; increment: () => void; } export const ClapContext = createContext<ClapContextType | undefined>(undefined); interface ClapProps { children: ReactNode; onClap: () => void }; const Clap = ({children, onClap}: ClapProps) => { const [count, setCount] = useState(0); const increment = () =>{ onClap(); setCount(count + 1); } const filteredChildren = Children.toArray(children).filter( child => isValidElement(child) ) as ReactElement[]; // ClapIcon 컴포넌트 const icon = filteredChildren.find(child => child.type === ClapIcon); // ClapCount 컴포넌트 const countDisplay = filteredChildren.find(child => child.type === ClapCount); // ClapTotal 컴포넌트 const totalDisplay = filteredChildren.find(child => child.type === ClapTotal); // Others 컴포넌트 const otherChildren = filteredChildren.filter(child => ![icon, countDisplay, totalDisplay].includes(child)); return ( <ClapContext.Provider value={{count, increment}}> <div> {icon} {countDisplay} {totalDisplay} </div> <Box>{otherChildren}</Box> </ClapContext.Provider> ); };
TypeScript
복사
contextAPI로 상태 전달하기
자식에게 직접 props를 넘겨줄 수도 있지만, 상황에 따라선 contextAPI를 활용하는 것이 유용할 수 있습니다.

Render Prop Pattern

NOTE
Render Prop Pattern은 HOC(High Order Component)와 코드의 재사용과 관심사의 분리라는 동일한 목적과 효과를 가지고 있지만, props을 활용한다는 점에서 차이가 있습니다.
<Title render={() => <h1>I am a render prop!</h1>} />
TypeScript
복사
render라는 props에 JSX를 반환하는 함수를 넣어주고 있다. 이러한 props를 우리는 render prop이라고 합니다.

목차

NOTE

1. React.Children

React.Children 유틸리티는 props.children을 다룰 때 유용합니다. 이 유틸리티는 map, forEach, count, only, toArray와 같은 메서드를 제공하여 자식 컴포넌트들을 쉽게 다룰 수 있도록 합니다.
React.Children.toArray: 자식 컴포넌트들을 배열로 변환합니다. 이는 키 충돌 없이 안전하게 자식 요소들을 다루고 싶을 때 유용합니다.
React.Children.map: props.children에 포함된 각 자식에 대해 함수를 실행하고 결과를 반환합니다. null이나 undefined 자식은 무시합니다.

2. isValidElement

React.isValidElement(object)는 주어진 객체가 React 요소인지 확인하는 함수입니다. 이는 컴포넌트 내에서 특정 자식이 유효한 React 요소인지 검증할 때 사용됩니다.

3. TypeScript에서의 타입 단언과 제네릭

TypeScript의 타입 단언과 제네릭은 React 컴포넌트에서 타입 안정성을 제공하는 데 필수적입니다.
Type Assertion: 컴파일러에게 특정 변수가 특정 타입임을 알릴 때 사용합니다. 예: const myCanvas = ref as HTMLCanvasElement;
Generics: 컴포넌트의 props나 state에 다양한 타입을 사용할 수 있게 해줍니다. 이는 컴포넌트가 다양한 타입을 수용할 수 있도록 유연성을 제공합니다.

4. Props 인터페이스

컴포넌트의 props를 정의할 때, TypeScript 인터페이스나 타입을 사용하여 각 prop의 타입을 명확히 정의합니다. 이는 오류를 줄이고, 자동 완성과 같은 IDE 기능을 향상시킵니다.
typescript코드 복사 interface ComponentProps { name: string; age?: number; } const MyComponent: React.FC<ComponentProps> = ({ name, age }) => { return <div>{name} {age}</div>; };
TypeScript
복사

5. 조건부 타이핑

React에서 조건부 렌더링을 할 때, TypeScript의 조건부 타입을 사용하여 더 안전하게 타입을 다룰 수 있습니다.
typescript코드 복사 interface Props { isEnable: boolean; data?: Data; // Data는 가상의 타입 } const MyComponent: React.FC<Props> = ({ isEnable, data }) => { return <div>{isEnable && data?.name}</div>; // data가 undefined일 수 있으므로 조건부 접근 사용 };
TypeScript
복사
이러한 요소들을 활용하면 React에서 보다 강력하고 타입 안전한 합성 컴포넌트를 구현할 수 있습니다. TypeScript를 사용함으로써 컴포넌트의 예측 가능성과 유지보수성을 크게 향상시킬 수 있습니다.

1. 목차

NOTE