src 폴더 하위에 components 폴더를 생성한다. components 폴더 하위에 여러 폴더를 생성한다.
이미지 파일을 위해 src 폴더 하위에 assets 폴더를 생성하고 jpg 파일을 넣어둔다. js 파일에서 이미지파일을 가져오기 위해 import를 사용한다(react 플젝 설정에서 지원). 이미지 파일을 import하면 완성된 앱에 이미지가 포함되도록 변환된다. 앱은 서버에 배포하여 해당 이미지에 대한 링크를 만들 수 있다. 링크는 번들링된 코드에 동적으로 삽입된다.
import testImg from '@/assets/test.jpg';
const Header = () => {
return (<>
<img src={testImg} alt="Test"/>
</>);
};
JSX 코드에서도 SVG 코드를 사용할 수 있다. 일반적인 컴포넌트 js 파일과 동일하게 사용할 수 있다.
개인적인 취향의 영역이지만, 하나의 도메인 폴더 안에서 기능 단위로 나누어 관리하기 위함이다.
import classes from './Card.module.css';
const Card = props => {
return <div className={classes.card}>{props.children}</div>;
};
export default Card;
단순한 래퍼 컴포넌트로서 약간의 html, css 코드를 가지며 원하는 컨텐츠(텍스트 등)를 보여주는 일반적인 공통 컴포넌트를 만들었다. 실제 프로젝트에서 재사용가능하기 위해 미리 스타일링된 유틸리티 컴포넌트인 Input, Button 컴포넌트도 래퍼 컴포넌트로서 개발된다.
import classes from './Input.module.css';
const Input = props => {
return (
<div className={classes.input}>
<label htmlFor={props.input.id}>{props.label}</label>
<input {...props.input} />
</div>
);
};
export default Input;
스프레드 연산자를 사용하여 input 태그 안에 들어갈 속성과 값을 자동매핑시켜줄 수 있다.
const 컴포넌트 = props => {
return (
<Input
label="Amount"
input={{
id: props.id,
type: 'number',
min: 1,
max: 5,
step: 1,
defaultValue: 1,
}}
/>
);
};
input props값은 객체이며, 이 객체에는 실제 input 태그에 내장되어 사용되는 속성들과 값을 넣어준다.
import { createPortal } from 'react-dom';
import classes from './Modal.module.css';
const Backdrop = () => {
return <div className={classes.backdrop} />;
};
const ModalOverlay = props => {
return (
<div className={classes.modal}>
<div className={classes.content}>{props.children}</div>
</div>
);
};
const overlaysEl = document.getElementById('overlays');
const Modal = props => {
return (
<>
{createPortal(<Backdrop />, overlaysEl)}
{createPortal(<ModalOverlay>{props.children}</ModalOverlay>, overlaysEl)}
</>
);
};
export default Modal;
Portal을 사용하여 index.html의 <body>
안에 <div id="app" />
위에 <div id="overlays" />
를 생성하여 실제 돔을 이 div에서 렌더링하도록 설정한다.
context api를 사용하여 modal 컴포넌트의 동작을 구현할 수 있으나 props drilling을 사용한 이유는 특정 기능을 위한 context api가 특정 컴포넌트에 묶여 한정적인 역할을 하기 때문이다. modal의 backdrop에 여러 props를 두면 여러 번의 props를 거치더라도 특정 유스케이스에 묶이지 않게되어 재사용성이 더 커진다.
앱의 여러 곳에서 필요하게 되는 state가 있는 경우는 props 보다 context api를 사용하는 것이 더 낫다.
앱 수준에서 state가 여러 개 있는 큰 앱에서 작업할 때, 컴포넌트 단위에서 관리하면 엄청나게 커질 뿐더러 불필요하게 많은 stateful/stateless component가 생길 것이다. 이를 관리하는 전용 store 컴포넌트가 있는 것이 좋다.
reducerFn에서 새로운 배열을 반환하기 위해 push()보다 concat()을 사용해야한다.
커스텀 컴포넌트에서 ref의 값을 갖고올 수 없으므로, 커스텀 컴포넌트의 컴포넌트 함수에 forwardRef를 래핑해준다.
import { forwardRef } from 'react';
import classes from './Input.module.css';
const Input = forwardRef((props, ref) => {
return (
<div className={classes.input}>
<label htmlFor={props.input.id}>{props.label}</label>
<input ref={ref} {...props.input} />
</div>
);
});
export default Input;
input의 ref 값은 input type이 숫자인 경우와 상관없이 항상 문자열이다.