useReducer
// reducer 함수와 초기상태를 받아서 dispatch 함수를 반환한다.
const [state, dispatch] = useReducer(reducer, initialState);
reducer 함수
현재 state와 action을 받아서 새로운 상태를 반환하는 함수이다. 상태 변경 로직을 포함하고 있고 switch 문이나 if 문을 사용하여 action.type에 따라 상태를 업데이트한다.
dispatch 함수
action을 reducer 함수에 전달하여 state를 변경하는 함수이다. 컴포넌트에서 이벤트 핸들러나 기타 로직을 통해 호출한다.
action : 액션은 상태를 변경하는 명령을 포함한 객체로 보통 type과 추가 데이터를 포함한다.
state : 현재 컴포넌트의 데이터를 나타내는 객체이다. reducer 함수가 반환하는 새로운 state로 업데이트한다.
// reducer 폴더 -> counterReducer.ts
const counterReducer = (state: number, action: { type: string }) => {
switch (action.type) {
case "increament":
return state + 1;
case "decreament":
return state - 1;
default:
return state;
}
};
// counter.tsx
import { useReducer } from "react";
import { counterReducer } from '../reducer/counterReducer';
const Counter = () => {
const [counter, dispatch] = useReducer(counterReducer, 0);
return (
<div className="counter">
<h1>{counter}</h1>
<div>
<button onClick={() => dispatch({ type: "decreament" })}>-</button>
<button onClick={() => dispatch({ type: "increament" })}>+</button>
</div>
</div>
);
};
export default Counter;
✏️ 위처럼 reducer 함수 counterReducer와 초기값 0을 설정하고 해당 dispatch type을 action에 전달하며 조건에 맞는 action.type의 로직이 실행되고 그에 따라 새로운 상태가 반환된다.
Context Api
import React, { createContext, useContext } from 'react';
const MyContext = createContext<string>(""); // 컨텍스트 생성
// 컨텍스트 제공하는 컴포넌트
const App = () => {
return (
// MyContext.Provider를 사용하여 컨텍스트의 값을 제공합니다.
<MyContext.Provider value="Hello, useContext!">
<MyComponent />
</MyContext.Provider>
);
};
export default App;
// 컨텍스트를 사용하는 컴포넌트
const MyComponent = () => {
const value = useContext(MyContext); // useContext 훅을 사용하여 MyContext의 현재 값을 가져옵니다.
return <div>{value}</div>;
};
✏️ 컨텍스트를 생성해주고 Provider로 그 데이터 값이 전역으로 제공해주면 해당 데이터 값이 필요한 컴포넌트들은 useContext 훅을 이용해 컨텍스트를 구독하면 전역 데이터 값을 사용할 수 있다.
🧐 여기서 Props drilling을 알고 넘어가자면
React의 데이터 흐름은 부모 컴포넌트에서 자식 컴포넌트로 가는 단방향 데이터 흐름이다.
상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하기 위해서 props를 계속해서 넘겨주는 과정이 Props drilling 이다.
🔍 예를들면 A부터 Z까지 알파벳 컴포넌트가 있다고 가정하고 A라는 컴포넌트의 state을 Z라는 자식 컴포넌트가 사용하고 싶다면 A~Z사이에 있는 모든 컴포넌트는 A컴포넌트의 state가 필요없어도 Props로 계속 전달해 줘야 한다.
그런데 여기서 A 컴포넌트의 state가 변경된다면?
모든 자식 컴포넌트는 리렌더링 되어 성능면에 좋지 않고 여러 컴포넌트를 거치다보니 유지보수 또한 어려움이 생긴다.
그래서 전역 상태관리를 통해 state를 필요한 컴포넌트에 효율적으로 전달하고 불필요한 리렌더링을 줄일 수 있다.