컴포넌트 Props
Props 개념
리액트에서는 부모 컴포넌트가 자식 컴포넌트에 데이터를 전달할 수 있습니다. 이때 사용하는 것이 바로 props(properties)입니다. props는 컴포넌트를 마치 HTML 태그처럼 사용해 값을 속성 형태로 전달합니다.
부모는 자식에게 props를 이용하여 데이터를 전달할 수 있다. props는 HTML의 속성처럼 컴포넌트의 태그 안에 입력되며, 여러개의 속성을 전달해도 항상 props 하나의 객체로 전달 된다.
Props 전달 기본
기본 문자/표현식
문자는 따옴표로, 숫자나 표현식은 중괄호에 넣어 부모 컴포넌트에서 다음과 같이 전달한다.
// 부모
export default function App() {
return(
<Header name="엄노니" age={30} />
);
}
// 자식
export default function Header(props: { name: string; age: number }) {
return(
<>
<h1>name: { props.name }</h1>
<h1>age: { props.age }</h1>
</>
);
}
자식 컴포넌트 측에서는 매개변수로 props를 받아 사용할 수 있다. props가 객체로 전달되므로 props.name과 같은 형태로 객체의 값을 출력하여 사용하면 된다.
객체
부모 측에서 전달하려는 속성 값이 여러개일 경우 속성을 따로따로 정의하는 것이 번거로울 수 있는데, 이럴 때는 하나의 객체로 값을 묶어 전달할 수 있다.
//부모
export default function App() {
const userProps = { name: "엄노니", age: 30 };
return(
<Header userProps={ userProps } />
);
}
//자식
export default function Header(props: { userProps: { name: string; age: number; }; }) {
return(
<>
<h1>name: { props.userProps.name }</h1>
<h1>age: { props.userProps.age }</h1>
</>
);
}
함수
props에는 값 뿐만 아니라 이벤트 핸들러 함수도 전달이 가능하다.
매개변수와 리턴값이 없는 함수의 경우 매개변수 형태는 () => void로 받는다.
//부모
export default function App() {
const userProps = { name: "엄노니", age: 30 };
const clickHandler = () => {
console.log('clicked!');
}
return(
<Header userProps={ userProps } clickHandler={ clickHandler }/>
);
}
//자식
export default function Header(props: { userProps: { name: string; age: number; }; clickHandler: () => void }) {
return(
<>
<h1>name: { props.userProps.name }</h1>
<h1>age: { props.userProps.age }</h1>
<button onClick={ props.clickHandler }>클릭</button>
</>
);
}
Props 전달 응용
구조 분해 할당
자식 컴포넌트의 매개변수가 복잡하므로 구조 분해 할당을 이용해 정리하면 좋다.
먼저 매개변수의 props를 { userProps, clickHandler }로 구조 분해 할당한다.
// 자식
export default function Header({ userProps, clickHandler }: { userProps: { name: string; age: number; }; clickHandler: () => void }) {
return(
<>
<h1>name: { userProps.name }</h1>
<h1>age: { userProps.age }</h1>
<button onClick={ clickHandler }>클릭</button>
</>
);
}
userProps는 객체이므로, 한 번 더 { name, age }로 구조 분해 할당한다.
이제 name, age, clickHandler 형태로 JSX 내부에서 사용할 수 있게 되었다.
// 자식
export default function Header({ userProps: { name, age }, clickHandler }: { userProps: { name: string; age: number; }; clickHandler: () => void; }) {
return(
<>
<h1>name: { name }</h1>
<h1>age: { age }</h1>
<button onClick={ clickHandler }>클릭</button>
</>
);
}
구조 분해 할당부 분리
props는 꼭 매개변수 위치에서 구조 분해 할당 하지 않아도 되고, 함수 내부에 분리가 가능하다.
구조 분해 할당한 부분을 다시 props로 대체한 뒤, return문 위쪽으로 분리해준다.
// 자식
export default function Header(props: { userProps: { name: string; age: number; }; clickHandler: () => void; }) {
const { userProps: { name, age }, clickHandler } = props;
return(
<>
<h1>name: { name }</h1>
<h1>age: { age }</h1>
<button onClick={ clickHandler }>클릭</button>
</>
);
}
interface 사용
좀 더 매개변수 선언을 줄이기 위해 ts의 interface를 사용할 수 있다.
interface가 있는 파일은 src/types에 위치시키는 것이 관례이며, interface는 따로 import 없이 모든 파일에서 접근이 가능하다(설정에 따라 다를 수 있음).
// UserProps.d.ts
interface UserProps {
userProps: { name: string; age: number; };
clickHandler: () => void;
}
// 자식
export default function Header(props: UserProps) {
const { userProps: { name, age }, clickHandler } = props;
return(
<>
<h1>name: { name }</h1>
<h1>age: { age }</h1>
<button onClick={ clickHandler }>클릭</button>
</>
);
}
전개연산자 사용
부모 측에서 전개 연산자를 쓰면 하나로 묶었던 객체를 분해해서 전달할 수 있다.
<Header userProps={ userProps } ~로 전달했던 부분을 <Header { ...userProps }~로 변경한다.
⭐userProps= 사라지는 것 확인!
interface와 자식 측 구조 분해 할당 코드도 전개 연산자 사용에 맞추어 변경해준다.
//부모
export default function App() {
const userProps = { name: "엄노니", age: 30 };
const clickHandler = () => {
console.log('clicked!');
}
return(
<Header { ...userProps } clickHandler={ clickHandler }/>
);
}
//UserProps.d.ts
interface UserProps {
name: string;
age: number;
clickHandler: () => void;
}
//자식
export default function Header(props: UserProps) {
const { name, age, clickHandler } = props;
return(
<>
<h1>name: { name }</h1>
<h1>age: { age }</h1>
<button onClick={ clickHandler }>클릭</button>
</>
);
}
children
컴포넌트는 <Header /> 형태로 바로 닫히는 태그를 사용할 수도 있지만, <Header></Header> 형태로 열고 닫는 태그 형태로도 사용할 수 있다.
부모 측에서 컴포넌트 태그 내부에 다른 요소를 추가하는 경우, props의 children으로 값이 전달된다.
형태는 React.ReactNode이다.
자식 측에서는 props.children을 사용해서 전달받은 콘텐츠를 출력할 수 있다.
그리고 내용이 여러 요소로 구성되어있어도 children 하나로만 전달이 되고, children은 2개 이상 존재할 수 없다.
//부모
export default function App() {
return(
<Header>
<p>hello world!</p>
<p>bye world!</p>
</Header>
);
}
//자식
export default function Header({ children }: { children: React.ReactNode }) {
return(
<>
{ children }
</>
);
}
children을 사용해도 다른 props 값을 사용할 수 있다.