함수형 컴포넌트에서는 앞서 공부한 라이프 사이클 메서드를 사용할 수 없다.
React v16.8부터는 함수형 컴포넌트에서도 클래스형 컴포넌트의 라이프 사이클 메서드와 유사한 기능을 할 수 있는 기능들을 제공한다.
1. useState
가장 기본적인 hook. 가변적인 상태를 지닐 수 있다.
(1) 사용 예
- state를 컴포넌트에 추가할 때
- 이전 state에서 상태값 업데이트가 필요할 때
- 배열이나 객체 state를 업데이트 할 때
- 초기값을 재생성하는 것을 막고 싶을 때
- key와 함께 state를 초기화 시키고 싶을 때
- 이전 렌더링 정보를 저장하고 싶을 때
(2) useState(initialState)
컴포넌트 최상단에 state 변수를 할당하며, 컨벤션은 일반적으로 [이름, set이름] 이런식으로 배열 구조 분해를 사용한다.
import {useState} from 'react';
const MyComponent = () => {
const [something, setSomething] = useState('Hello');
}
[파라미터 | 매개변수]
- initialState : 상태를 초기에 지정할 값이다.
- 모든 유형이 값이 될 수 있지만 함수에는 특별한 동작이 있다.이 인수는 초기 렌더링 후 무시된다.
- 만약 함수를 인수로 넣게 될 경우, 이 함수는 init 함수로서의 역할을 한다. 이 때 이 함수는 순수함수여야 하고, 매개변수가 없고 반환 값이 반드시 있어야 하는 특징이 있다.
- React는 컴포넌트를 초기화 하고 싶을 때 이 init 함수를 호출하고 반환 값을 초기 상태로 지정한다.
[반환 | return]
useState는 2가지 value를 리턴한다.
- 1) 현재의 state 값을 리턴하는데, 최초 렌더링 시에는 초기에 선언한 initialState 값이 리턴된다.
- 2) set 함수를 리턴한다. 이 함수는 값이 변경될 때, state를 업데이트 시키고 재렌더링을 트리거한다.
[유의사항]
- useState도 Hook 의 일종이기 때문에, 컴포넌트의 최상단에서 선언해주어야 한다. 루프 안에서 혹은 조건문 안에서 선언하면 안되고, 만약 이런 것들이 필요한 상황이라면 새로운 컴포넌트를 만들어서 state를 넘겨주는 방식을 사용해야 한다.
- Strict Mode에서 리액트는 초기(initializer) 함수가 순수함수가 아닌 경우를 감지하기 위해 initializer Function을 두 번 호출 할 것이다. 이는 개발 환경에서만 동작하며, 프로덕션에는 영향을 미치지 않는다. 초기 함수가 순수한 경우(순수해야 함) 동작에 영향을 주지 않아야 한다. 호출 중 하나의 결과는 무시된다.
2. set 함수 (setSomething) (nextState)
set에서 반환한 함수를 사용하면 상태(state)를 다른 값으로 업데이트하고 다시 렌더링을 트리거할 수 있다.
setState 함수를 사용하여 다음 상태를 직접 전달하거나, 이전 상태에서 계산하는 함수를 전달할 수 있다.
const [name, setName] = useState('Rachel');
const [age, setAge] = useState(20);
function handleClick() {
setName('Taylor');
setAge(a => a + 1);
// ...
[파라미터 | 매개변수]
- nextState : 업데이트 함수. 순수함수여야 함. 변경되어야 할 상태값만 인자로 가져올 수 있다. 그리고 다음 state값(변경된 혹은 업데이트 된)을 반환해야 한다. React는 이 setState 함수(업데이트 함수)를 큐에 넣어두었다가 컴포넌트를 재렌더링 시킨다.
다음 렌더링 동안 React는 이전 상태와 비교하여 큐(대기열)에 걸린 업데이트를 적용하여 다음 상태값을 계산한다.
[반환 | returns]
setState 함수는 반환값이 없다.
[유의사항]
1. setState 함수는 다음 렌더링을 위한 상태 값만 업데이트 한다.
- setState 함수가 호출 된 후에 state 값을 읽으면, 호출 전 화면에 있던 이전 값(old value)이 보일 것이다.
2. 새롭게 업데이트 된 값이 현재 state와 같다면, 리액트는 해당 컴포넌트와 그 자식 컴포넌트들의 재 렌더링을 건너뛴다.
3. React는 상태 업데이트를 일괄 처리한다.
- 모든 이벤트 핸들러가 실행되고 setState 함수가 호출되고 난 후 화면을 업데이트한다. 이는 단일 이벤트에 여러 렌더링이 발생하는 것을 막을 수 있다. 물론, DOM에 엑세스 하기 위해 React가 화면을 일찍 업데이트 하도록 강제해야 하는 경우도 있다.
4. 렌더링이 되는 동안 set 함수를 호출하는 것은 현재 렌더링되는 컴포넌트 내에서만 허용된다.
- React는 현재 렌더링하고 있던 ouput을 버리고 즉시, 새로운 state 값을 반영한 컴포넌트를 그릴 것이다.
(이 패턴은 거의 사용할 일은 드물지만, 이전 렌더링의 정보를 저장할 때 유용하다)
5. Strict Mode에서 업데이트 기능을 두 번 호출한다.
3. 사용법
1) Basic Examples
- Counter : https://github.com/BoraParkDev/react-example/blob/main/src/useState/Counter.tsx
- Text Field(string) : https://github.com/BoraParkDev/react-example/blob/main/src/useState/TextFields.tsx
- CheckBox(boolean) : https://github.com/BoraParkDev/react-example/blob/main/src/useState/Checkbox.tsx
- Form(Two variables) : https://github.com/BoraParkDev/react-example/blob/main/src/useState/Form.tsx
2) 이전 State에서 상태값 업데이트 하기 (prevState Updating)
- 업데이터*를 사용하는 것이 항상 좋을까?
: setAge(a+1) 대신 setAge(a => a+1)로 사용하라는 것처럼 느껴질 수 있다. 이 방법은 필수로 요구되는 방법이 아니며, 대부분의 경우에서 두 접근 방식에 대해 차이가 없다. React는 항상 유저의 의도된 action(예를 들면 클릭같은..)에 반응하기 때문에 다음 클릭 전에 state 변수가 업데이트되도록 항상 확인한다 . age즉, 클릭 핸들러가 이벤트 핸들러 시작 부분에서 "stale 상태"를 볼 위험이 거의 없다 . - 그러나, 동일 이벤트 내에 여러 업데이트가 발생할 경우에는 업데이터 함수가 도움이 될 수 있다.
- * 업데이터 : Updater function / pending 된 state를 가져와서 다음 state를 계산하는 함수.React에서는 이 업데이터 함수를 큐(대기열)에 올리고, 다음 렌더링 동안 순차적으로 호출한다.
- 예시)
- Updater Function을 사용하는 경우
import { useState } from 'react';
export default function Counter() {
const [age, setAge] = useState(42);
function increment() {
setAge(a => a + 1);
}
return (
<>
<h1>Your age: {age}</h1>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<button onClick={() => {
increment();
}}>+1</button>
</>
);
}
- 사용하지 않는 경우
import { useState } from 'react';
export default function Counter() {
const [age, setAge] = useState(42);
function increment() {
setAge(age + 1);
}
return (
<>
<h1>Your age: {age}</h1>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<button onClick={() => {
increment();
}}>+1</button>
</>
);
}
-> 이 경우는 항상 +1만큼 밖에 업데이트가 되지 않는다. 그 이유는 이미 실행 중인 코드에서 state 변수는 set 함수를 호출한다고
업데이트되지 않기 때문이다.
3) Object 형 또는 Array형인 State 값 업데이트하기
- React에서는 state가 읽기 전용으로 간주되므로 기존 개체를 변경하는 대신, state를 교체해야 한다.
- 예시)
-
//이렇게 form 객체가 state 값으로 할당되었을 경우, 이런식으로 변경하면 안된다. form.firstName = 'Taylor'; //새로운 객체를 생성해서 변경해주어야 한다. //spread 연산자를 사용하게 되면 상태 객체가 변형되지 않고 교체된다. setForm({ ...form, firstName: 'Taylor' })
- Form(Object) : https://github.com/BoraParkDev/react-example/blob/main/src/useState/Object/index.tsx
- Form(nested Object) : https://github.com/BoraParkDev/react-example/blob/main/src/useState/Object/Nested.tsx
- List Array : https://github.com/BoraParkDev/react-example/tree/main/src/useState/Todo
- Concise Update Logic with Immer :
- immer를 사용해서 반복되는 코드를 줄이는 방법도 있다.
- 대신 package를 설치해야 함.
npm i immer
npm i use-immer
4) 초기 값(initial state) 재 생성 피하기
'Frontend > React' 카테고리의 다른 글
React Study - React.memo() (0) | 2023.07.28 |
---|---|
Vite에서 React.StrictMode 환경에 따라 설정하기 (0) | 2023.05.24 |
React Study - 7. 리액트의 라이프사이클(class형 컴포넌트) (0) | 2023.04.26 |
React Study - 5. ref: DOM에 이름 달기 (0) | 2023.04.16 |
React Study - 4. 이벤트 핸들링 (0) | 2023.04.13 |