react의 상태관리 툴 중 하나 cf ) ContextAPI, Recoil, MobX, Jotai...
npm install redux // 일반 설치 npm install react-redux // react 사용시 설치 npm install @reduxjs/toolkit react-redux // redux toolkit = 기존 redux 개선버전 (뭄법 좀 더 쉬움) // 기존 redux만 쓰면 redux-actions, redux-thunk 등 기능들을 // 설치해서 사용해야되는데 이 번거로움을 해결해줌 // 사용 방법 redux-saga와 약간 다르니까 다음에 더 찾아보기.
🌱 단방향으로 데이터가 흐르게 하는 Flux아키텍처 아이디어를 잘 구현함.
Store
에서는 다시 Reducer
에 Action
을 전달함.Reducer
는 State
를 가공해서 새로운 State
를 Store
로 전달함.// index.js
import { Provider } from "react-redux";
import { createStore } from "redux";
// action 객체를 리턴하는 함수 생성
const increase = () =>{
return {
type : "INCREASE"
};
};
const decrease = () =>{
return {
type : "DECREASE"
};
};
const count = 1;
// reducer 함수 생성
const countReducer = (state = count, action) => {
switch (action.type){
case "INCREASE" :
return state + 1;
case "DECREASE" :
return state - 1;
case "SET_NUMBER" :
return action.payload;
default :
return state;
}
}
const rootEle = document.getElementById("root");
const root = createRoot(rootEle);
//reducer함수 생성 후 createStore에 전달까지 해야 reducer함수를 통해 상태변경 준비 완료.
const store = createStore(countReducer)
root.render(
<Provider store={store}> //전역관리를 위해 App 컴포넌트를 감싸줌.
<App />
</Provider>
)
Store
내부 상태값의 안정성을 유지하기 위함.순수함수여야 함. (외부 요인으로 인해 기대한 값이 아닌 엉뚱한 값으로 상태가 변경되는 일이 없어야하기 때문)
초기 상태 값과 액션(Action)을 인자로 받아 액션에 조작할 상태(State)를 지정함.
첫 번째, 두 번째 파라미터 각각 = (현재 state값
, action(객체)
) => {..
reducer가 여러개인 경우 = combineReducers 으로 해결
- 기능에 따른 reducer를 나누어서 작성하면 코드를 유지보수하는데 효율적임.
// action.js
export const ADD_TODO = "ADD_TODO";
export const COMPLETE_TODO = " COMPLETE_TODO ";
export const SHOW_ALL = "SHOW_ALL";
export const SHOW_COMPLETE = "SHOW_COMPLETE";
// {type : ADD_TODO, text : '할일'}
export function addTodo(text) {
return {
type: ADD_TODO,
text: text,
};
}
//{type: COMPLETE_TODO,. index : 3}
export function completeTodo(index) {
return {
type: SHOW_ALL,
index: index,
};
}
//{type: COMPLETE_TODO,. index : 3}
export function showCompletel() {
return {
type: SHOW_COMPLETE,
};
}
export function showAll() {
return {
type: SHOW_ALL,
};
}
// reducer.js
// 여러개인 경우 예시
import { conbineReducers } from "redux";
import todos from "./todos"; // reducer 함수 1
import filter from "./filter"; // reducer 함수 2
const reducer = combineReducers({todos : todos, filter : filter });
// combineReducers를 이용하여 todos, filter 두 가지의 reducer를 결합함.
// 전달된 action의 type값에 따라 둘 중 해당하는 reducer 함수로 전달함.
export default reducer;
// ⚡️ action.type 값이 ADD_TODO 또는 COMPLETE_TODO 인경우 todos reducer가 동작
// todos.js
import { ADD_TODO, COMPLETE_TODO } from "./actions";
const initialState = [];
// todosInitialState = [{text : '산책', done : false}, {text : '점심먹기', done : false}]
export default function todos(previousState = initialState, action) {
if (action.type === ADD_TODO) {
return [...previousState, { text: action.text, done: false }];
}
if (action.type === COMPLETE_TODO) {
return previousState.todos.map((todo, index) => {
if (index === action.index) {
return { ...todo, done: true };
}
return todo;
});
}
return previousState;
}
// ⚡️ action.type 값이 SHOW_ALL 또는 SHOW_COMPLETE 인경우 filter reducer가 동작
// filter.js
import { SHOW_ALL, SHOW_COMPLETE } from "./actions";
const initialState = "ALL";
export default function filter(previousState = initialState, action) {
if (action.type === SHOW_COMPLETE) {
return "COMPLETE";
}
if (action.type === SHOW_ALL) {
return "ALL";
}
return previousState;
}
type
사용 (type
에 따라 변화)// action ex)
// payload가 필요 없는 경우 // payload 필요한 경우
const action = { const action = {
type : "INCREASE" type : "INCREASE"
} payload : 10 ;
}
// 액션 생성자(Action Creater) = action 객체를 반환하는 함수
const addTodo = () => {
return {
type : "ADD_TODO",
data : {
id : 1,
text : "Redux add todo"
}
}
Reducer
로 Action
전달하는 함수.dispatch
의 인자로 action
객체 담아서.// reducer 함수로 해당 객체 전달
dispatch({type:"INCREASE", payload:5});
// reducer 함수로 action 객체 전달
dispatch(action);
// 액션 생성자를 사용하여 전달하는 경우
dispatch(addTodo(3))
react-redux 라이브러리에서 action
객체를 reducer
에 전달하고 변경된 state
의 변경사항을 다른 컴포넌트에게 알려주는 역할을 하는 hooks를 지원해줌.
💥 useDispatch : reducet
로 action
객체 전달.
💥 useSelector : 컴포넌트와 state
연결해서 redux
의 state
에 접근할 수 있게 해주는 메서드.(state가져오기)
// dispatch 사용 예시
// App.js
import { useDispatch, useSelector } from "react-redux";
import { increase, decrease } from "./index"
const App = () => {
// store 객체의 dispatch 역할
const dispatch = useDispatch(); // store의 dispatch 함수사용.
// store 객체의 getState() 역할
const state = useSelector((state)=>state.countReducer); // store의 state값을 가져오는 역할
const plusBtn = () => { dispatch(increase()) };
const minusBtn = () => { dispatch(decrease()) };
return (
<div>
<p>{`Count : ${state}`}</p>
<button onClick={plusBtn}> + </button>
<button onClick={minusBtn}> - </button>
</div>
)
}
export default App;
dispatch
함수를 감싸는 역할을 수행함.store
의 dispatch
함수로 주입됨.dispatch
함수에 action
객체를 담아 호출하게 되면 맨 마지막에 감싸진 middleware가 action
을 전달받아 작업을 수행하고, 작업을 마치면 다음 감싸진 middleware를 실행하는 동작을 반복함. reducer
로 action
이 전달되어 state
가 업데이트 됨.yarn add redux-promise-middleware
reducer
에게 전달함.action
생성함수는 payload로 promise객체 자체를 실어 보내므로 reducer
에 state
를 정상적으로 전달할 수 없음.yarn add redux-thunk
action
생성자가 반환하는 개체로는 처리하지 못했던 작업을 함수로 반환할 수 있게 됨.yarn add redux-logger
reducer
가 실행되기 전과 실행된 후를 log로 편리하게 확인+비교 가능.