Redux는 가장 대표적인 전역 상태 관리 라이브러리로, 상태(state)를 중앙에서 관리
Store에 보관
UI : 사용자가 버튼 클릭 등 상태변화가 필요한 이벤트 발생Action : 상태 변경을 위한 정보를 담은 객체 생성Dispatch : Action을 Reducer로 전달하는 함수Reducer : Action을 해석하여 그 값에 따라 Store의 전역 상태 변경Store : 전역 상태가 저장되는 저장소, Reducer에 의해 변경된 상태는 UI에서 리렌더링 상태를 어떻게 변경할지 정의하는 객체
const increment = { type: "INCREMENT", payload: 5 };
또는 함수를 사용하여 동적으로 액션을 생성할 수도 있음
const increment = (num) => ({ type: "INCREMENT", payload: num });
Action을 Reducer로 넘겨주는 함수
dispatch({ type: "INCREMENT", payload: 5 });
dispatch(increment(5)); // 액션 생성자 함수 사용
Action을 받아 상태를 변경하는 함수
const counterReducer = (state, action) => {
switch(action.type) { // 어떤 액션 타입인지 검사
case 'INCREMENT' // 액션 타입의 값에 따라 다른 값을 리턴
return state + action.payload // 새로운 state값을 전달
... ...
}
}
Redux의 전역 상태 저장소 , createStore 함수에 Reducer를 전달해서 생성
const store = createStore(counterReducer);
Reducer가 여러 개일 경우 combineReducers로 묶어서 사용
const rootReducer = combineReducers({ counter: counterReducer });
const store = createStore(rootReducer);
<Provider store = {store}>useSelector()useDispatch()터미널에서 redux와 react-redux 설치
npm install redux react-redux
📁 App.jsx
import "./App.css";
function App() {
return (
<>
<div>counter : 0</div>
<button>+</button>
<button>-</button>
</>
);
}
export default App;
📁 redux.js
const increment = { type: "increment" };
const decrement = { type: "decrement" };
📁redux.js
const counterReducer = (state = 0, action) => {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return state;
}
};
📁 redux.js
const store = createStore(counterReducer);
📁 main.jsx
import { Provider } from "react-redux";
import { store } from "./redux";
createRoot(document.getElementById("root")).render(
<Provider store={store}>
<App />
</Provider>
);
App.jsx)import { useSelector } from "react-redux";
function App() {
const counter = useSelector((state) => state);
return (
<>
<div>counter : {counter}</div>
<button>+</button>
<button>-</button>
</>
);
}
App.jsx)import { useDispatch, useSelector } from "react-redux";
function App() {
const counter = useSelector((state) => state);
const dispatch = useDispatch();
return (
<>
<div>counter : {counter}</div>
<button onClick={() => dispatch(increment)}>+</button>
<button onClick={() => dispatch(decrement)}>-</button>
</>
);
}
const increment1 = {
type: 'increment1'
}
const decrement1 = {
type: 'decrement1'
}
const counterReducer1 = (state = 0, action) => { // counter의 초기값은 0
switch (action.type) {
case 'increment1' :
return state + 1 // 새로운 상태 반환
case 'decrement1' :
return state - 1
default :
return state // 예외처리
}
}
const increment2 = {
type: 'increment2'
}
const decrement2 = {
type: 'decrement2'
}
const counterReducer2 = (state = 0, action) => { // counter의 초기값은 0
switch (action.type) {
case 'increment2' :
return state + 2 // 새로운 상태 반환
case 'decrement2' :
return state - 2
default :
return state // 예외처리
}
}
const rootReducer = combineReducers({counterReducer1, counterReducer2})
export const store = createStore(rootReducer)
const counter1 = useSelector((state) => state.counterReducer1);
const counter2 = useSelector((state) => state.counterReducer2);
function App() {
const counter1 = useSelector((state) => state.counterReducer1);
const counter2 = useSelector((state) => state.counterReducer2);
const dispatch = useDispatch();
return (
<>
<div>
<h3>counter 1</h3>
<div>counter : {counter1}</div>
<button onClick={() => dispatch(increment1)}>+</button>
<button onClick={() => dispatch(decrement1)}>-</button>
</div>
<div>
<h3>counter2</h3>
<div>counter : {counter2}</div>
<button onClick={() => dispatch(increment2)}>+</button>
<button onClick={() => dispatch(decrement2)}>-</button>
</div>
</>
);
}
Redux에서 비동기 작업(API 호출, setTimeout 등)을 처리할 수 있도록 해주는 미들웨어
npm install redux-thunk
applyMiddleware(thunk)를 Store에 추가applyMiddleware를 이용해 연결하고 싶은 미들웨어 연결App.jsximport "./App.css";
import { applyMiddleware, combineReducers, createStore } from "redux";
import { useDispatch, useSelector } from "react-redux";
import { thunk } from 'redux-thunk';
export const store = createStore(rootReducer, applyMiddleware(thunk));
<div>
<h3>함수로 전달하기 : Thunk</h3>
<div>counter : {counter1}</div>
<button onClick={() => dispatch((dispatch) => {
setTimeout(() => {
dispatch(increment1)
}, 1000)
})}>+</button>
<button onClick={() => dispatch((dispatch) => {
setTimeout(() => {
dispatch(decrement1)
}, 1000)
})}>-</button>
</div>
onClick 이벤트가 발생하면 dispatch가 실행dispatch 내부에 또 다른 dispatch가 들어가 있음 ((dispatch) => {...})dispatch에 함수를 전달하면, Redux에서 이를 실행할 때 해당 함수를 호출setTimeout(() => { dispatch(increment1); }, 1000);increment1 액션을 디스패치