State
이전 글에서 소개한 리액트의 props는 props를 사용하는 컴포넌트를 호출하는 부모 컴포넌트에서 그 값을 전달해주고, 해당 컴포넌트는 그 값을 변경할 수 없다. 반면에 state는 컴포넌트 내부에서 변경될 수 있는 값을 의미한다.
2021.11.11 - [React] - React) props란?
React) props란?
props란 properties의 약어로 컴포넌트 속성을 설정할 때 사용하는 요소이다. props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있고, 해당 컴포넌트 자신은 이를 변경할 수
u-u002.tistory.com
리액트에서는 컴포넌트를 생성하는 방식이 두 가지 존재한다. 컴포넌트 종류에 따라 클래스형 컴포넌트에서는 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
07. spread 와 rest 문법 · GitBook
07. spread 와 rest 이번에는 ES6 에서 도입된 spread 와 rest 문법에 대해서 알아보겠습니다. 서로 완전히 다른 문법인데요, 은근히 좀 비슷합니다. spread 일단 spread 문법부터 알아봅시다. spread 라는 단어
learnjs.vlpt.us
이로써 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 |