State
이전 글에서 소개한 리액트의 props는 props를 사용하는 컴포넌트를 호출하는 부모 컴포넌트에서 그 값을 전달해주고, 해당 컴포넌트는 그 값을 변경할 수 없다. 반면에 state는 컴포넌트 내부에서 변경될 수 있는 값을 의미한다.
2021.11.11 - [React] - React) props란?
리액트에서는 컴포넌트를 생성하는 방식이 두 가지 존재한다. 컴포넌트 종류에 따라 클래스형 컴포넌트에서는 state 그대로, 함수형 컴포넌트에서는 useState 함수를 이용해서 state를 사용할 수 있다.
1. 클래스형 컴포넌트의 state
클래스형 컴포넌트에서는 생성자 내부에서 'this.state = { }'의 형태(객체의 형태)로 정의할 수 있다. 아래의 코드를 보자. state을 JSX에서 사용할 때에도 원래 props와 마찬가지로 Javascript 코드임을 나타내는 '{ }' 내부에서 'this.state.변수명'의 형태로 사용해야 하나, 앞서 배운 비구조화 할당 방식(const { key } = this.state;)으로 코드를 줄였다.
아래 코드에서 확인할 수 있는 점은 아래와 같다.
1) 클래스형 컴포넌트에서 state을 사용하기 위해서는 생성자(constructor)를 통해 설정한다.
2) 클래스형 컴포넌트에서 생성자를 작성할 때는 반드시 'super(props);'를 선언해주어야 한다.
- 생성자가 호출되면 (클래스형 컴포넌트들은 항상 상속받아야 하는) Component 클래스의 생성자 함수를 호출해준다.
import React, { Component } from 'react';
class CustomComponet extends Component {
constructor(props) {
super(props);
this.state = {
key:value
}
}
render() {
const { key } = this.state; // 비구조화 할당 방식
return (
<>
{key}의 형태로 조회 가능합니다.
<button
onClick={() => {
this.setState({ key2: value2 });
}
}
> ...
</button>
</>
);
}
}
export default CustomComponent;
앞서 보인 코드에서 좀 더 간결하게 만들 수 있다. 생성자 내부에 this.state으로 초기화하던 것을 단순히 state라는 키워드를 통해 초기화할 수 있다.
import React, { Component } from 'react';
class CustomComponet extends Component {
state = {
key:value
}
render() {
const { key } = this.state; // 비구조화 할당 방식
return ( ... );
}
}
export default CustomComponent;
※ 유의할 점
1) 리액트의 컴포넌트 정의 시 위와 같이 HTML 태그 상에 이벤트를 설정할 경우에는 화살표 함수 문법을 통해 넣어야 한다.
2) this.setState(객체)는 전달받은 키의 값만을 추가/변경한다. 다른 키의 값들은 사라지거나 변경되지 않는다. 즉, 이미 있는 state에 해당 사항을 merge하는 것이지 새로운 state으로 갈아치우는 것이 아니다.
3) 만약 onClick 이벤트 내부에서 특정 key의 state의 값을 연달아 두 번 변경한다면 어떨까? 누적된 결과가 나올까? 답은 '아니다'. state의 값은 바로바로 변경되지 않는다. 즉, 비동기적이다. 때문에 만약 바로 연달아 값에 변화를 주고 싶다면, onClick내부의 setState에 인자로서 객체를 전달하여 특정 key값의 변화를 줄 것이 아니라, 아예 인자로 함수를 전달하는 것이 옳다. (반드시 리턴해주자!!)
onClick = {
setState((prevState, props) => {
return { ... } // 여러 로직을 거칠 때
}
}
onClick = {
setState((prevState, props) => ({
// 값을 바로 반환할 때
}));
}
- 위 함수의 prevState는 이전 상태를 의미하며, 이전 상태에 변화를 주고자할 때 사용할 수 있다. props는 현재 지니고 있는 propst로, 사용하지 않다면 인자로 받지 않아도 무관하다.
- 만약 setState를 통해 값 변경 후 특정 작업을 수행하고 싶다면, 두 번째 파라미터로 콜백 함수를 전달할 수 있다.(props가 없을 때 두 번째라는 의미)
onClick = {() => {
this.setState({ ... }, () => {
// callback function logic
});
}
onClick = {() => {
this.setState((prevState) => {
return {
// state 값에 변화를 주는 함수
}
}, () => {
// callback function logic
});
}
2. 함수형 컴포넌트의 state
React v16.8 부터는 함수형 컴포넌트도 useState 함수를 통해 state를 사용할 수 있게 됐다. 하지만 그 방법이 클래스형 컴포넌트와는 다르니 알고 있자. 함수형 컴포넌트가 state를 사용하기 위해서는 Hooks라는 것이 필요하다. useState는 이 Hooks 중에 하나라는 점을 염두하자.
배열 비구조화 할당
이는 앞서 props 관련 포스트에서 언급했던 객체 비구조화 할당과 매우 유사하다. 다만, 배열의 값을 쉽게 추출할 수 있게 한다는 것이 다르다.
// 배열 arr의 값을 변수에 할당하는 기본적인 방법
const arr = ['a', 'b'];
const aChar = arr[0];
const bChar = arr[1];
// 배열 비구조화 할당 방식을 사용한 예
const arr = ['a', 'b'];
const [aChar, bChar] = arr;
useState
useState 함수의 인자에는 초깃값을 전달해준다. 클래스형 컴포넌트에서는 이 초깃값이 객체의 형태여야 했으나, useState 함수를 사용한 함수형 컴포넌트에서는 객체 형태가 아니어도 된다.(문자열, 숫자, 객체, 배열 무엇이든 상관없다.) 아래 코드에서는 useState을 사용해 초깃값을 ''로 정의해주고, 두 개의 원소를 가진 배열을 할당하고 있다. 이 때, 첫 번째 원소는 state의 현재 상태를 의미하고, 두 번째 원소는 state의 상태를 바꿔주는 Setter function 이다.
즉, char라는 state 키의 값을 ''로 초기화 한후, setChar 함수를 통해 그 값을 변화시킬 수 있는데, 아래 코드에서는 버튼 두 개를 두고 위의 버튼을 클릭할 시 char 키의 값을 'a'로, 아래의 버튼을 클릭할 시 char 키의 값을 'A'로 바꾸는 것이다. 이게 은근 헷갈린다..^^
import React, { useState } from 'react';
const CustomComponent = () => {
const [char, setChar] = useState('');
const onClickEnter = () => setChar('a');
const onClickLeave = () => setChar('A');
return (
<>
<button onClick={onClickEnter}>'a'로 바꾸기</button>
<button onClick={onClickLeave}>'A'로 바꾸기</button>
</>
)
}
export default CustomCoponent;
3. State 사용시 주의 사항
state를 사용할 때는 컴포넌트의 종류와 상관없이 주의해야 하는 점이 있다. setState 함수 혹은 useState 함수를 통해 전달 받은 Setter 함수 이 두 가지의 방식으로만 그 값을 변경해야 한다.
또한, 배열이나 객체를 업데이트할 때에는 그 배열 및 객체의 사본을 만들고 그 사본의 값을 변경한 후 그 사본의 상태를 setState 혹은 useState 함수를 통해 전달 받은 Setter 함수를 사용해 업데이트 해야 한다.(객체 사본 생성 시에는 spread 연산자를 사용하고 배열은 내장 함수를 사용한다.)
const originObj = {a: 1, b: 2, c: 3};
const newObj = {...obj, b: 3}; // originObj의 사본을 만들되 b 키의 값만 변경한다.
const originArr = [
{id: 1, value: true},
{id: 2, value: false},
{id: 3, value: false}
]
let newArr = arr.concat({id: 4, value: true}); // 새 원소 추가
newArr.filter(item => item.id !== 2); // id가 2인 원소 제거
newArr.map(item => item.id === 1 ? {...item, value: false} : item)); // id가 1인 원소의 값만 'false'로 변경한다.
cf. spread 연산자 : '...객체명'의 형태로 사용하며 사본을 생성하는 함수로 아래 '벨로퍼트와 함께하는 모던 자바스크립트' 블로그의 포스트를 첨부한다.
https://learnjs.vlpt.us/useful/07-spread-and-rest.html
이로써 props와 state에 대한 포스팅을 마쳤다. props라고 해서 항상 고정된 값은 아니며, 실제로 어플리케이션 개발 중에는 부모 컴포넌트의 state 값이 자식 컴포넌트의 props 값으로 전달될 수 있다. 이렇게 될 경우에는 자식 컴포넌트의 특정 상황에 따라 부모 컴포넌트의 state 값이 변경되게 된다면 자식 컴포넌트로 전달되는 props 역시 변경될 수 있게 된다.
'Frontend > React' 카테고리의 다른 글
React) 리액트의 대표적인 Hook - useState, useEffect (0) | 2022.05.31 |
---|---|
React) 불변성, State의 가장 큰 특징 (0) | 2022.05.27 |
React) props란? (0) | 2021.11.11 |
React) 컴포넌트(함수형 vs 클래스형), JSX 문법 (0) | 2021.11.02 |
React) 배포하기 위해 Build하기 (0) | 2021.11.02 |