React를 다루는 기술 04 - props와 state
props는 properties의 줄임말로 컴포넌트 속성을 설정할 때 사용되는 요소로 부모 컴포넌트에서 설정하여 자식 컴포넌트에서 받아서 사용할 수 있다.
JSX 내부에서 props 렌더링
const MyComponent = props => {
return <div>안녕하세요. 제 이름은 {props.name}입니다.</div>
}
위의 코드는 name이라는 props를 렌더링하도록 하는 코드이다.
props 값은 함수 컴포넌트의 파라미터를 통해 받아와서 사용할 수 있으며 함수 내 다른 변수와 마찬가지로 {}로 감싸서 사용할 수 있다.
해당 props의 값은 부모 컴포넌트에서 설정할 있으며 해당 코드는 아래와 같다.
const parentComponent = () => {
return <MyComponent name="John" />
}
이렇게 입력하면 자식 컴포넌트인 MyComponent의 props.name의 값은 John이 되고 결과는 안녕하세요. 제 이름은 John입니다.
가 된다.
props의 기본값 설정: defaultProps
위의 부모 컴포넌트에서 props값 설정을 하지 않는다면 어떻게 될까?
자식 컴포넌트에서는 받은 값이 없으므로 {props.name}
은 undefined
가 되고 React는 렌더링 시 undefined
는 false와 동일하게 보기 때문에 아무런 값도 렌더링되지 않는다.
이러한 문제를 방지하기 위해 props의 변수에 기본값을 설정할 수 있는 defaultProps가 있다.
const MyComponent = props => {
return <div>안녕하세요. 제 이름은 {props.name}입니다.</div>
};
MyComponent.defaultProps = {
name: 'John'
}
위와 같이 정의해두면 부모 컴포넌트에서 props값이 정의되지 않을 경우 defaultProps에서 지정해둔 값을 사용하게 된다.
태그 사이의 내용을 보여주는 children
children은 부모 컴포넌트 태그 사이의 값을 보여주는 props 변수이다.
const parentComponent = () => {
return <MyComponent name="John">React</MyComponent>
}
const MyComponent = props => {
return (
<div>안녕하세요. 제 이름은 {props.name}입니다.</div>
<div>이 프로젝트는 {props.children}입니다.</div>
)
}
// 안녕하세요. 제 이름은 John입니다.</div>
// 이 프로젝트는 React입니다.
부모 컴포넌트에서 자식 컴포넌트를 렌더링 할 때 그 태그 사이에 값을 입력할 경우
자식 컴포넌트에서는 이를 children으로 받을 수 있다.
그리하여 부모 컴포넌트에서 children이라는 props값을 설정하지 않았음에도 자식 컴포넌트에서 children으로 태그 사이의 값을 받아서 출력하는 것을 볼 수 있다.
이러한 방식을 응용하여 다른 컴포넌트들의 묶음을 자식 컴포넌트에 children 변수 하나로 합쳐서 넘겨줄 수 있다.
비구조화 할당을 이용한 props 내부 값 추출하기
지금까지는 자식 컴포넌트에서 props값을 사용할 때 항상 props.
을 앞에 붙여서 호출했다.
이 번거로운 작업을 덜어내기 위해 ES6의 비구조화 할당 문법을 사용하여 내부의 값을 바로 추출해낼 수 있다.
const parentComponent = () => {
return <MyComponent name="John">React</MyComponent>
}
const MyComponent = props => {
const {name, children} = props;
return (
<div>안녕하세요. 제 이름은 {name}입니다.</div>
<div>이 프로젝트는 {children}입니다.</div>
)
}
이렇게 객체에서 값을 추출하는 문법을 비구조화 할당(destructuring assignment)이라고 부른다.
이 문법은 구조 분해 문법이라고도 불리며 함수의 파라미터 내에서도 사용 가능하다.
const parentComponent = () => {
return <MyComponent name="John">React</MyComponent>
}
const MyComponent = ({name, children}) => {
return (
<div>안녕하세요. 제 이름은 {name}입니다.</div>
<div>이 프로젝트는 {children}입니다.</div>
)
}
이렇게 하면 props값을 쓸 때마다 props를 붙일 필요가 없이 간단하게 사용할 수 있다.
propTypes를 통한 props검증
컴포넌트에서 필수 props를 지정하거나 props가 받아야하는 특정 타입을 지정할 때 사용하는 것이 propTypes이다.
propTypes는 React처럼 우선 import하여 사용해야한다.
import PropTypes from 'prop-types';
const MyComponent = ({name, children}) => {
return (
<div>안녕하세요. 제 이름은 {name}입니다.</div>
<div>이 프로젝트는 {children}입니다.</div>
)
}
MyComponent.propTypes = {
name: PropTypes.string
}
위와 같이 선언하면 name prop에는 string 이외의 타입의 값이 정의될 때마다 console창을 통해 에러 메시지를 뱉는다.
prop의 값이 필수로 존재해야한다는 것을 지정할 때에는 isRequired를 뒤에 붙여주면 된다.
import PropTypes from 'prop-types';
const MyComponent = ({name, children}) => {
return (
<div>안녕하세요. 제 이름은 {name}입니다.</div>
<div>이 프로젝트는 {children}입니다.</div>
)
}
MyComponent.propTypes = {
name: PropTypes.string,
children: PropTypes.string.isRequired
}
이제 children에 값이 정의되지 않으면(undefined) 콘솔창에서 에러를 뱉는다.
이 외에 propTypes의 종류는 이하와 같다.
- array: 배열
- arrayOf: 특정 PropType으로 이루어진 배열
- bool: boolean값
- func: 함수
- number: 숫자
- object: 객체
- string: 문자열
- symbol: ES6의 Symbol
- node: 렌더링 할 수 있는 모든 것
- instanceOf(클래스) : 특정 클래스의 인스턴스
- oneOf([‘dog’, ‘cat’]) : 주어진 배열 요소의 값 중 하나
- oneOfType([React.PropTypes.string, React.PropTypes.number]): 주어진 배열 안의 종류 중 하나
- objectOf(React.PropTypes.number) : 객체의 모든 키 값이 인자로 주어진 propType인 객체
- shape({name: PropTypes.string, num: PropTypes.number}) : 주어진 스키마를 가진 객체
- any : 아무 종류나
State
React에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다.
props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이며 자식 컴포넌트는 이 값을 직접 변경하여 정의할 수 없다.
state는 이와 달리 해당 컴포넌트가 정의하며 직접 변경하여 사용할 수 있다.
함수형 컴포넌트에서 state를 사용하기 위해서는 React 16.8버전부터 추가된 hook이라는 기능에 포함된 useState 함수를 사용해야한다.
import React, {useState} from 'react';
const MyComponent = () => {
const [name, setName] = useState('John');
return (
<div>안녕하세요. 제 이름은 {name}입니다.</div>
)
}
위와 같이 useState 함수를 import 해온 뒤 컴포넌트 내에서 사용하여 ‘John’이라는 값으로 정의 된 name이라는 state변수를 생성하였다.
여기서 중요한 부분이 state 변수를 선언할 때의 [name, setName]
부분인데 이러한 방식을 배열 비구조화 할당이라고 한다.
useState는 입력된 값을 기본값으로 가지는 state변수와 해당 변수를 정의할 수 있는 setter인 함수를 배열형태로 반환한다.
때문에 [name, setName]
과 같은 형태로 변수를 선언할 경우 이를 차례대로 받아 name이라는 state함수와 setName이라는 setter 함수를 정의하게 된다.
useState는 하나의 함수형 컴포넌트 내에서 여러번 사용해도 상관없으므로 여러개의 state변수가 필요할 경우 여러차례 사용하여 state변수를 생성할 수 있다.
state를 사용할 때 주의사항
state를 수정할 때는 반드시 useState를 통해 받은 setter함수를 사용해야한다.
만약 배열이나 객체를 업데이트해야 하는 경우에는 그 사본을 생성한 뒤 해당 사본을 담은 변수를 setter함수를 통해 정의하면 된다.
let nextArray = array.concat({ id: 4 }); // 새 항목 추가
nextArray.filter(item => item.id !== 2); // id가 2인 항목을 제거
nextArray.map(item.id === 1 ? {...item, value: false}); // id가 1인 경우 해당 항목의 value를 false로 변경
위의 ...item
은 spread 연산자라고 하며 {...item, value: false}
는 value키에는 false값을 할당하며 그 외의 구성요소는 그대로 입력한다는 뜻이다.
출처자료
https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=209354690 (리액트를 다루는 기술(개정판))