Redux는 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너
useState : 간단한 상태 업데이트
useReducer : dispatch 함수를 이용한 상태 업데이트
리덕스를 쓰면
1. store를 만들어서 상태를 관리할 수 있다.
2. devtools를 써서 상태를 확인할 수 있다.
3. middleware를 사용할 수 있다.
function reducer(state,aciton){
return 새로운state값;
}
하나의 스토어를 작성한다.
스토어 안에는 현재의 상태, 리듀서와 내장함수 dispatch()
, subscribe()
, getState()
가 있다.
const store= Redux.createStore(reducer함수)
<Provider store={store}>
<App/>
<Provider>
store를 전역에서 사용할 수 있도록 Provider로 감싼다.
store.getState()
type 속성을 가진 객체인 action을 전달한다.
이 외의 속성을 추가할 수도 있다.
store.dispatch({
type: '타입명'
})
액션을 만들어주는 함수 작성하여 상태값 변경 시 아래와 같이 변경할 수 있다.
function increment(){
return{
type: "INCREMENT",
add: add
}
};
store.dispatch(
increment(5)
);
store가 업데이트 되면 함수를 다시 호출한다.
store.subscribe(호출할 함수);
<head>
src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.2.0/redux.min.js"></script>
</head>
<body>
<h1 id="number">0</h1>
<button id="increment">+</button>
<button id="decrement">-</button>
<script>
const numberEle= document.querySelector("#number");
const btnIncerment= document.querySelector("#increment")
const btnDecerment= document.querySelector("#decrement")
// 초기값 설정
const initialState={
num: 0
}
// 1. reducer 함수 만들기
function reducer(state=initialState,action){
switch(action.type){
case 'INCREMENT':
return{
num: state.num+1
};
case 'DECREMENT':
return{
num: state.num-1
};
default:
break;
}
}
// 2. store 생성
const store= Redux.createStore(reducer);
// 상태가 변경될 때 마다 호출시킬 함수
const render=()=>{
numberEle.innerHTML= store.getState().num;
}
// 3. 구독하기 : store가 업데이트 되면 (render)함수를 다시 호출
store.subscribe(render);
// 이벤트 지정
btnIncerment.addEventListener("click",()=>{
store.dispatch({type:"INCREMENT"});
});
btnDecerment.addEventListener("click",()=>{
store.dispatch({type:"DECREMENT"});
});
</script>
</body>
npm install redux
및 npm install react-redux
npm install redux-thunk
npm install @redux-devtools/extension
이 되어있음을 기반으로 한다.
액션 타입, 액션 생성함수, 리듀서가 모두 포함되어있는 JS 파일을 생성한다.
// action type 만들기 : redux module
const SET_DIFF= "counter/SET_DIFF";
const INCREMENT= "counter/INCREMENT";
// action 생성 함수 만들기
export const setDiff= (diff)=>({type:SET_DIFF, diff});
export const increase=()=>({type:INCREMENT});
// 초기 상태 선언
const initialState={
number:0,
diff:1
}
// reducer 선언
export default function counter(state=initialState,action){
switch (action.type) {
case SET_DIFF:
return{
...state,
diff: action.diff
};
case INCREMENT:
return{
...state,
number: state.number+state.diff
};
default:
return state;
}
}
import { combineReducers } from "redux";
import counter from "./counter";
import todos from "./todos";
// 여러개의 useReducer를 한개로 합치기
const rootReducer= combineReducers({
counter: counter,
todos: todos
})
export default rootReducer;
[index.js]
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals';
import rootReducer from './modules';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
// 스토어 생성
const store= createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
// App을 Provider로 감싸고 생성한 스토어를 props로 전달한다.
<Provider store={store}>
<App/>
</Provider>
</React.StrictMode>
);
reportWebVitals();
❗ createStore 작성시 취소선이 그어질 수 있으나 정상적으로 작동된다.
이 취소선을 없애고 싶다면 import 시 import{legacy_createStore as createStore} from 'redux';
로 작성한다.
1. 프리젠테이셔널 컴포넌트
리덕스 스토어에 접근하지 않고 필요한 값은 props로 전달 받아, 화면을 설계하는 컴포넌트
// 프리젠테이셔널 컴포넌트
import React from 'react';
const Counter = ({number,diff,onIncrease,onDecrease,onsetDiff}) => {
const onchange=(e)=>{
onsetDiff(e.target.value*1);
}
return (
<div>
<h1>{number}</h1>
<div>
<input type="number" value={diff} min="1" onChange={onchange}/>
<button onClick={onIncrease}>+</button>
</div>
</div>
);
};
export default Counter;
2. 컨테이너 컴포넌트
리덕스 스토어의 상태를 조회하거나 액션을 디스패치할 수 있는 컴포넌트
// 컨테이너 컴포넌트
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Counter from '../components/Counter';
import { increase, setDiff } from '../modules/counter';
const CounterContainer = () => {
// redux store의 상태를 조회하는 Hook useSelector
// useSelector()로 받아온 값에는 모든 redux store 상태가 들어가있다.
// 이 중 counter에 들어간 값을 받아오고 싶을 경우 리듀서 합치기에서 작성한
// combineReducers의 key인 counter를 부르면 된다.
const {number,diff}= useSelector(state=>({...state.counter}));
const dispatch= useDispatch();
// 각 액션들을 dispatch하는 함수
const onIncrease=()=> dispatch(increase());
const onsetDiff=()=> dispatch(setDiff(diff));
return (
<Counter number={number} diff={diff}
onIncrease={onIncrease} onsetDiff={onsetDiff}
/>
);
};
export default CounterContainer;
Redux DevTools 다운로드
createStore()의 매개변수로 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
입력
개발자 도구(F12) 👉 Redux로 실행
위와 같이 실행하면 state가 변경될 때마다 action과 state의 변경사항을 안내해준다.
Redux DevTools 다운로드
터미널에서 npm install --save @redux-devtools/extension
install
createStore()의 매개변수로 devToolsEnhancer()
입력, import 되는 Hook임
개발자 도구(F12) 👉 Redux로 실행
위와 같이 실행하면 state가 변경될 때마다 action과 state의 변경사항을 안내해준다.