import { useReducer } from 'react';
function reducer(state, action) {
// 여기서 state 는 최신 상태의 스테이트를 가져온다
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, 초기값);
// ...
useState의 대체 함수이다.
이 Hook 함수를 사용하면 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있다. 상태 업데이트 로직을 컴포넌트 바깥에 작성 할 수도 있고, 심지어 다른 파일에 작성 후 불러와서 사용 할 수 있음.
useReducer에 의해서 반환되는 디스패치 함수는 state를 다른 값으로 업데이트하고 리렌더를 트리거할 수 있게 해준다. 인자로는 전달할 함수와, 스테이트값을 넣을 수 있다.
const [state, dispatch] = useReducer(reducer, 스테이트 초기값);
function handleClick() {
dispatch({ type: 'incremented_age', 리듀서 함수로 전달시킬 데이터 });
// ...
첫번째 인자로 들어간 값을 리듀서 함수의 매개변수로 써서,
function reducer(state, action) {
// ...
}
🚨 디스패치는 다음 렌더링에서 변화될 스테이트 값을 업데이트한다. 그렇기 떄문에 디스패치를 호출했다고 해서 바로 아래에 스테이트를 찍어봐야 현재 상태의 스테이트 값만 나오니 주의!!
다음은 인강을 보면서 useReducer를 사용해본 예제이다
const reducer = (state, action) => {
switch (action.type) {
case "INIT": {
return action.data;
}
case "CREATE": {
const created_date = new Date().getTime();
const newItem = {
...action.data,
created_date,
};
return [newItem, ...state];
}
case "REMOVE": {
return state.filter((item) => item.id !== action.targetId);
}
case "EDIT": {
return state.map((item) =>
item.id === action.targetId
? { ...item, content: action.newContent }
: item
);
}
default:
return state;
}
};
function App() {
const [data, dispatch] = useReducer(reducer, []);
// ...
const onCreate = useCallback((author, content, emotion) => {
dispatch({
type: "CREATE",
data: { author, content, emotion, id: dataId.current },
});
dataId.current += 1;
}, []);
// ...
}
앱 컴포넌트 내부에서 useReducer를 호출하고, 상태 변화가 필요한 곳에서 dispatch를 가져와 type과 최신 데이터(payload)를 reducer함수에 매개변수로 전달한다. 그리고 컴포넌트 외부에 있는 reducer함수를 실행하는데, 스위치 문을 이용해서 조건에 해당하는 액션을 실행한다.
디스패치를 사용할 때 인자를 하나만 넘기는 예제도 있고, 두 개를 넘기는 예제도 있어서 조금 더 검색해봤다.
ex1) action type만 정의하여 사용
<button onClick={() => dispatch({ type: "INCREMENT" })}>증가</button>
ex2) action type과, 데이터를 정의하여 사용
<button onClick={() => dispatch({ type: "INCREMENT", payload: 1 })}>증가</button>
이제 여기서 리듀서로 넘어가고,
ex1) action type만 정의하여 사용
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1 };
case "DECREMENT":
return { count: state.count - 1 };
default:
throw new Error("unsupported action type: ", action.type);
}
}
ex2) action type과, 데이터를 정의하여 사용
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { count: state.count + action.payload };
case "DECREMENT":
return { count: state.count - action.payload };
default:
throw new Error("unsupported action type: ", action.type);
}
}

이전에 코드를 작성하면서 전역적으로 함수를 넘기기 위해 이 함수를 사용하지도 않는 컴포넌트에도 프롭으로 넘겼다. 이것을 props drilling(굉장히 비효율적이라는 뜻!!)라고 한다.
이를 해결하기 위해서 context 라는 개념이 생겼는데,

이처럼 모든 데이터를 가진 컴포넌트(App)가 Provider 라는 공급자 역할을 하는 컴포넌트에게 자신이 가진 모든 데이터를 전달하고, 이 Provider가 자신의 자손에 해당하는 모든 컴포넌트들에 직통으로 데이터를 넘긴다.
export const MyContext = React.creatContext(defaultValue);
부모 컴포넌트에서 context 생성 후 App을 감싸고,
<MyContext.Provider value = {전역으로 전달하고자 하는 값}>
{/* 이 context 안에 위치할 자식 컴포넌트들 */}
<MyContext.Provider/>
자식 컴포넌트에서 useContext를 이용해 값을 받아온다
import { MyContext } from "../App";
export default function DiaryList({ props }) {
const diaryList = useContext(MyContext);
}
공부하기도 바쁜 시기인데 여러가지 방해 요소가 자꾸 겹쳐서 스트레스 😂😂😂😂