개발일지
1회차 발표 useState, useReducer 본문
• useState
컴포넌트에서 state변수를 추가할 수 있게 해주는 React 훅입니다.
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(28);
const [name, setName] = useState('Taylor');
const [something, setSomething] = useState(initialState)
// ...
배열 구조 분해를 사용하여 [something, setSomething]과 같은 state 변수의 이름을 지정해주어야 합니다!
매개변수
• initialState: 초기에 state를 설정할 값입니다.
반환값
useState는 두개의 값을 반환합니다.
1. 현재 state입니다. 첫 번째 렌더링 중에는 전달한 initialState와 같습니다
2. state를 다른 값으로 업데이트하고 리렌더링을 촉발할 수 있는 set 함수
사용시 주의사항
useState는 훅이므로 컴포넌트의 최상위 레벨이나 직접 만든 훅(Custom Hook)에서만 호출할 수 있습니다. 반복문이나 조건문 안에서는 호출할 수 없습니다.
set 함수
useState가 반환하는 set 함수를 사용하면 state를 다른 값으로 업데이트하고 리렌더링을 촉발할 수 있습니다.
const [name, setName] = useState('Edward');
function handleClick() {
setName('Taylor');
// ...
• nextState: state가 될 값입니다.
사용시 주의사항
set 함수는 다음 렌더링에 대한 state 변수만 업데이트합니다. set 함수를 호출한 후에도 state 변수에는 여전히 호출 전 화면에 있던 이전 값이 담겨 있습니다.
사용법
1. 컴포넌트에 state 추가하기
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(42);
const [name, setName] = useState('Taylor');
// ...
이 state 변수의 현재 state로, 처음에 제공한 초기 state로 설정됩니다.
상호작용에 반응하여 다른 값으로 변경할 수 있는 set 함수입니다.
function handleClick() {
setName('Robin');
}
2. 이전 state를 기반으로 state 업데이트하기
age가 42라고 가정합니다. 이 핸들러는 setAge(age + 1)를 세 번 호출합니다:
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
그러나 클릭해보면 age는 45가 아니라 43이 됩니다! 이는 set 함수를 호출해도 이미 실행 중인 코드에서 age state 변수가 업데이트되지 않기 때문입니다. 따라서 각 setAge(age + 1) 호출은 setAge(43)이 됩니다.
이 문제를 해결하려면 다음 state 대신 setAge에 업데이터 함수를 전달할 수 있습니다:
function handleClick() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}
여기서 a => a + 1은 업데이터 함수입니다. 이 함수는 대기 중인 state를 가져와서 다음 state를 계산합니다.
마지막 함수가 실행이 끝나면 대기 중인 다른 업데이트가 없으므로, React는 결국 45를 현재 state로 저장합니다.
3. 객체 및 배열 state 업데이트
state에는 객체와 배열도 넣을 수 있습니다. React에서 state는 읽기 전용으로 간주되므로 기존 객체를 변이하지 않고, 교체를 해야 합니다. 예를 들어, state에 form 객체가 있는 경우 변이하지 마세요:
form.firstName = 'Taylor';
새로운 객체를 생성하여 전체 객체를 교체하세요:
setForm({
...form,
firstName: 'Taylor'
});
4. 초기 state 다시 생성하지 않기
React는 초기 state를 한 번 저장하고 다음 렌더링부터는 이를 무시합니다.
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
// ...
createInitialTodos()의 결과는 초기 렌더링에만 사용되지만, 여전히 모든 렌더링에서 이 함수를 호출하게 됩니다.
이 문제를 해결하려면, 대신 이를 useState에 초기화 함수로 전달하세요:
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
// ...
함수를 호출한 결과인 createInitialTodos()가 아니라 함수 자체인 createInitialTodos를 전달하고 있다는 것에 주목하세요. 함수를 useState에 전달하면 React는 초기화 중에만 함수를 호출합니다.
*컴포넌트가 리렌더링될 때(input에 타이핑할 때 등)에는 실행되지 않습니다.
5. key로 state 재설정하기
이해가 안됩니다.. ....🥹
• useReducer
컴포넌트의 최상위 레벨에서 useReducer를 호출하여 reducer를 통해 state를 관리하세요.
import { useReducer } from 'react';
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...
매개변수
const [state, dispatch] = useReducer(reducer, initialArg, init);
• reducer: state가 업데이트되는 방식을 지정하는 reducer 함수입니다. state와 액션을 인자로 받아야 하고, 다음 state를 반환해야 합니다. state와 액션은 어떤 유형이든 가능합니다.
• initialArg: 초기 state가 계산되는 값입니다. 모든 유형의 값일 수 있습니다. 이 값에서 초기 state를 계산하는 방법은 다음 init 인자에 따라 달라집니다.
• init: 초기 state 계산 방법을 지정하는 초기화 함수입니다. 이것을 지정하지 않으면 초기 state는 initialArg로 설정됩니다.
*여기서 initialArg는 초기 값을 계산하기 위한 입력값이고, init은 그 초기 값을 기반으로 초기 상태를 계산하는 함수입니다.
반환값
1. 현재 state. 첫 번째 렌더링 중에는 init(initialArg) 또는 (init이 없는 경우) initialArg로 설정됩니다.
2. state를 다른 값으로 업데이트하고 리렌더링을 촉발할 수 있는 dispatch function
dispatch function?
React는 상태가 변경될 때 컴포넌트를 다시 렌더링합니다. 그러나, 만약 새로운 상태 값이 이전 상태 값과 동일하다면, 다시 렌더링할 필요가 없습니다. 이로 인해 불필요한 렌더링을 방지하고 성능을 최적화할 수 있습니다.
const [state, dispatch] = useReducer(reducer, { age: 42 });
function handleClick() {
dispatch({ type: 'incremented_age' });
// ...
매개변수
• action: 사용자가 수행한 작업입니다. 어떤 데이터 유형이든 올 수 있습니다. 관용적으로 액션은 보통 이를 식별하는 type 속성이 있는 객체이며, 선택적으로 추가 정보가 있는 다른 속성을 포함할 수 있습니다.
반환값
dispatch 함수에는 반환값이 없습니다.
주의사항
dispatch함수는 다음 렌더링에 대한 state 변수만 업데이트합니다. 만약 dispatch함수를 호출한 후 state 변수를 읽으면, 호출 전 화면에 있던 이전 값이 계속 표시됩니다.
React는 상태 비교에 JavaScript의 Object.is 메서드를 사용합니다. 이 메서드는 두 값이 동일한지 비교하는데, 이는 다음과 같은 경우에 두 값이 동일하다고 판단됩니다.
* Object.is
두 값이 모두 undefined, null, true, false, +0, -0, NaN, 문자열 또는 같은 객체를 참조하는 경우.
사용시 주의사항
useReducer 는 훅이므로 컴포넌트의 최상위 레벨 또는 자체 훅에서만 호출할 수 있습니다. 반복문이나 조건문 내부에서는 호출할 수 없습니다.
사용법
1. 컴포넌트에 reducer 추가하기
import { useReducer } from 'react';
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...
이 state 변수의 현재 state(state). 처음에 제공한 초기 state({ age: 42 })로 설정됨.
상호작용에 반응하여 이를 변경할 수 있는 dispatch(dispatch) 함수.
화면에 표시되는 내용을 업데이트하려면 사용자가 수행한 작업을 나타내는 객체, 즉,액션을 사용하여 dispatch를 호출합니다:
function handleClick() {
dispatch({ type: 'incremented_age' });
}
React는 현재 state와 액션을 reducer 함수(reducer)에 전달합니다. Reducer는 다음 state를 계산하고 반환합니다. React는 다음 state를 저장하고, 컴포넌트를 렌더링하고, UI를 업데이트합니다.
2. reducer 함수 작성하기
Reducer 함수는 다음과 같이 선언됩니다:
function reducer(state, action) {
// ...
}
그런 다음 다음 state를 계산하고 반환할 코드를 입력해야 합니다. 관례상 switch 문으로 작성하는 것이 일반적입니다. switch의 각 case 에 대해 다음 state를 계산하고 반환해야 합니다.
function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
};
}
case 'changed_name': {
return {
name: action.nextName,
age: state.age
};
}
}
throw Error('Unknown action: ' + action.type);
}
액션은 어떤 형태든 가질 수 있습니다. 관례상 액션을 식별하는 type 프로퍼티가 있는 객체를 전달하는 것이 일반적입니다. 여기에는 reducer가 다음 state를 계산하는 데 필요한 최소한의 필수 정보가 포함되어야 합니다.
function Form() {
const [state, dispatch] = useReducer(reducer, { name: 'Taylor', age: 42 });
function handleButtonClick() {
dispatch({ type: 'incremented_age' });
}
function handleInputChange(e) {
dispatch({
type: 'changed_name',
nextName: e.target.value
});
}
// ...
액션 유형 이름은 컴포넌트에 로컬로 지정됩니다. 각 액션은 아무리 많은 데이터를 변경하게 되더라도 오직 하나의 상호작용만을 기술합니다. state의 모양은 임의적이지만 일반적으로 객체나 배열이 될 것입니다.
2. 초기 state 재생성 방지하기
React는 초기 state를 한 번 저장하고 다음 렌더링에서 이를 무시합니다.
function createInitialState(username) {
// ...
}
function TodoList({ username }) {
const [state, dispatch] = useReducer(reducer, createInitialState(username));
// ...
createInitialState(username)의 결과는 초기 렌더링에만 사용되지만, 이후의 모든 렌더링에서도 여전히 이 함수를 호출하게 됩니다.
이 문제를 해결하려면 useReducer 세번 째 인수에 초기화 함수를 전달할 수 있습니다.
function createInitialState(username) {
// ...
}
function TodoList({ username }) {
const [state, dispatch] = useReducer(reducer, username, createInitialState);
// ...
함수를 호출한 결과인 createInitialState()가 아니라 함수 자체인 createInitialState를 전달하고 있다는 점에 유의하세요. 이렇게 하면 초기화 후에는 초기 state가 다시 생성되지 않습니다.
'리액트 공식문서 스터디' 카테고리의 다른 글
useRef (0) | 2024.05.30 |
---|---|
useContext (0) | 2024.05.28 |
useReducer (0) | 2024.05.22 |
useState (1) | 2024.05.17 |
props (0) | 2024.05.13 |