리액트에서 전역 상태를 관리할 때 많이 쓰는 Redux와 Context API를 사용법과 장단점 위주로 비교해보자
상태 관리는 View 중심으로 이루어진 React Component에서 변하는 값에 대한 상태를 관리한다
고 할 수 있다.
useState
를 사용하면 지역 상태 관리를 할 수 있다.사용하는 컴포넌트의 안 / props로 전달할 때
만 하위 컴포넌트에서 사용할 수 있다.useState
만을 사용해서 진행할 수도 있다.props
로 하나씩 내려주기에 한계가 있다.리액트 생태계에서 가장 사용률이 높은 상태 관리 라이브러리.
Redux
는 독립적으로 사용이 가능하고 자바스크립트 앱 안의 상태를 관리하기 위해 사용한다.
상태를 저장하는 store
을 따로 가지고 있다.
Redux
를 사용하면 컴포넌트들의 상태 관련 로직들을 다른 파일들로 분리시켜서 더욱 효육적으로 관리할 수 있고, 글로벌 상태 관리도 쉽게 할 수 있다.
Context API
와 useReducer Hook
을 사용해서 개발하는 흐름은 Redux
를 사용하는 것과 매우 유사하다.
리덕스가 많이 사용된다고 해서 무조건 우리의 프로젝트에 리덕스가 필요한 건 아니다.
물론, 잘 사용한다면 프로젝트 개발 생산성에 아주 큰 도움을 줄 수도 있다.
하지만 단순히 글로벌 상태 관리를 위한 것이면 Context API
를 활용하는 것으로 충분할 수 있다.
리덕스가 아무리 좋은 라이브러리라고 해도 취향에 맞지 않고 어렵게만 느껴지고 마음에 들지 않는다면 사용하지 않는 것이 좋다
export const focusChange = (payload) => {
return {
type: "FOCUS_CHANGE",
payload,
};
};
// action
// 유저 네임 변경하기, Post 추가하기
const changeUsername = (name) => {
return {
type: "CHANGE_NAME",
name,
};
};
const addPost = (post) => {
return {
type: "ADD_POST",
post,
};
};
action
을 type
으로 구별하여 상태를 업데이트하는 로직을 Reducer
로 작성한다.const initialState = {
startDate: null,
endDate: null,
focusedInput: START_DATE,
};
const datePickerReducer = (state=initialState, action) => {
switch (action.type) {
case "FOCUS_CHANGE":
return {
...state,
focusedInput: action.payload
};
default:
return state;
}
};
// state
const initState = {
name: 'yezo',
posts: [],
};
// reducer
const reducer = (prevState, action) => {
switch (action.type) {
case "CHANGE_NAME":
return {
...prevState,
name: action.name,
};
case "ADD_POST":
return {
...prevState,
posts: [...prevState.post, action.post],
};
default:
return prevState;
}
};
createStore
를 통해 store의 인자에 생성한 Reducer
를 넣어 생성한다.combineReducer
를 통해 Reducer를 하나로 합치는 과정을 추가로 진행 해야 한다.import { createStore } from 'redux'
// const { createStore } = require('redux');
const store = createStore(reducer, initState);
const store = createStore(reducer, initState);
console.log(store.getState()); // { name: 'yezo', posts: [] }
store.dispatch(changeUsername('차동동'));
console.log(store.getState()); // { name: '차동동', posts: [] }
store.dispatch(addPost('1'));
store.dispatch(addPost('앗....'));
store.dispatch(addPost('등록되나요?'));
console.log(store.getState()); // { name: '차동동', posts: ['1', '앗...', '등록되나요?'] }
store.dispatch(changeUsername('차보리'));
console.log(store.getState()); // { name: '차보리', posts: ['1', '앗...', '등록되나요?'] }
import { useSelector } from 'react-redux'
const state = useSelector(state => state.itemReducer);
React
에서만 사용 가능하다. (리액트 내장 기능)createContext
를 통해 상태를 저장하게 된다.initState
에 초기 상태값을 객체 형태로 넣으면 된다.import { createContext } from 'react'
const boardStateContext = createContext(initState);
export type Action = {
type: "UPDATE",
payload
};
const reducer = (state, action) => {
switch (action.type) {
case 'CREATE_USER':
return {
users: state.users.concat(action.user)
};
case 'TOGGLE_USER':
return {
...state,
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
};
case 'REMOVE_USER':
return {
...state,
users: state.users.filter(user => user.id !== action.id)
};
default:
return state;
}
}
useReducer
에서 상태값과 dispatch
를 value
로 사용할 수 있다.// State 용 Context 와 Dispatch 용 Context 따로 만들어주기
const UsersStateContext = createContext(null);
const UsersDispatchContext = createContext(null);
// 위에서 선언한 두가지 Context 들의 Provider 로 감싸주는 컴포넌트
export function UsersProvider({ children }) {
const [state, dispatch] = useReducer(usersReducer, initialState);
return (
<UsersStateContext.Provider value={state}>
<UsersDispatchContext.Provider value={dispatch}>
{children}
</UsersDispatchContext.Provider>
</UsersStateContext.Provider>
);
}