state 관리를 위한 redux
액션이란 이벤트를 통해 상태를 관리하고 업데이트하기 위한 패턴 및 라이브러리인 redux.
예측 가능한 방식으로만 업데이트 될 수 있도록 하는 규칙과 함께 전체에서 사용해야 하는 상태에 대한 중앙저장소 역할을 함.
Context API와 비슷한데 왜 Redux를 더 많이 사용할까?
1. 전체적으로 사용하는 state가 언제 어디서 어떻게 업데이트 되는지 자세하게 추적이 가능하다.
2. Context API와 다르게 redux는 미들웨어가 있어서 비동기처리가 가능하다.
- redux의 reducer가 state를 바로 반환하지 못하고, 미들웨어를 거쳐야 반환이 가능하다
- 예) 로그인 시도 -> 미들웨어 (성공액션, 실패액션, 에러액션) -> 결과에 따른 state 반환
Redux 공식 사이트 : https://ko.redux.js.org/introduction/getting-started/
redux 라이브러리 설치
$ npm init -y
$ npm install redux react-redux
$ npm install redux-devtools-extension
// dev툴과 연결해주는 라이브러리
redux 세팅 - 생성
reducer를 사용하기 위한 state, action을 넣고,
action안의 type에 따라 state 값이 달라지는 기능을 switch문으로 짜서 넣었다.
type은 오타가 나면 찾기 어려운 string대신 변수로 만들어 넣었다.
//reducer.js
const initialState = {number: 0}
const UP = "COUNTER/Up"
const DOWN = "COUNTER/Down"
export const up = () => ({type:UP})
export const down = () => ({type:DOWN})
const rootReducer = (state = initialState, action) => {
switch(action.type){
case UP:
return{
...state,
number:state.number+1
}
case DOWN:
return{
...state,
number:state.number-1
}
default:
return state
}
}
redux 세팅 - dispatch를 호출해서 state값을 바꾸기
state 값을 바꿀 dispatch를 사용하기 위해 reducer에서 만들었던 변수를 가져왔다.
Context API에서는 임의로 dispatch를 만들어야했지만 redux는 useDispatch메서드로 만들 수 있다.
useSelector메서드는 리액트에 존재하는 모든 state값을 가져와 counter 변수에 저장해준다.
//index.js
import {up,down} from "../../reducers"
import {useSelector,useDispatch} from 'react-redux'
const Counter = () => {
const counter = useSelector((state)=>state)
const dispatch = useDispatch()
const onUp = () => {dispatch(up())}
const onDown = () => {dispatch(down())}
return (
<>
<h2>Counter : {counter.number}</h2>
<button onClick={onUp}>+1</button>
<button onClick={onDown}>-1</button>
</>
)
}
redux 세팅 - Store 생성
redux는 전역함수가 아니라 상태라는 것을 기억하자.
redux는 객체를 반환하는 함수이며, 다음과 같은 세가지 속성이 자주 쓰인다.
- createStore : function
- applyMiddleware : function
- combineReducers : function
const redux = require('redux')
const {createStore} = redux //state
현재는 리액트가 업데이트 되어서 createStore 대신 configureStore을 사용한다.
지금은 미들웨어를 쓰지 않을것이라서 미들웨어는 빼뒀다.
Store 컴포넌트 내부 컴포넌트들이 Store가 가진 데이터를 쓸 수 있도록
store(고정변수)에 configureStore로 생성한 store를 넣어주었다.
Store 컴포넌트 내부 컴포넌트들을 바깥에서도 볼 수 있도록 children으로 내부 속성값을 사용.
// useStore.jsx
import {configureStore} from "@reduxjs/toolkit"
const store = configureStore({
reducer : rootReducer, // 사용할 리듀서 정의
// middleware: [...middlewares], // 사용할 미들웨어 정의
// 기본적으로 composeWithDevTools이 적용되어있음
})
const Store = ({children}) => {
return (
<Provider store={store}>
{children}
</Provider>
)
}
전역으로 데이터를 관리해야한다면 가장 최상위로 가야한다.
Store 컴포넌트로 최상위 App 컴포넌트를 감싸주었다.
//index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import Store from './store/useStore'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Store>
<App />
</Store>
</React.StrictMode>
);
잘 생성되었는지 체크 - 만약 +1 버튼을 클릭힌다면
button의 onClick이 발동되면서 onUp메서드가 실행되고, dispatch(up())메서드가 실행된다.
type을 UP으로 입력받은 reducer 컴포넌트에선 본래 state에서 number속성에 1을 더한 값을 반환한다.
//reducer.jsx
export const up = () => ({type:UP})
case UP: return { ...state, number:state.number+1 }
useSelector는 갱신된 state값을 받아서 다시 counter에 넣어준다.
//index.jsx
const counter = useSelector((state) => state)
initialState:{number:0}으로 초깃값을 정해놓았으므로 counter.number는 0에 1을 더한 1이 된다.
만들어놓은 Store 컴포넌트는 이런 흐름이 가능하도록 데이터를 전역적으로 저장해주고있다.
<h3>Counter : {counter.number}</h3>
'프로그래밍 > react' 카테고리의 다른 글
[220504] 미들웨어) redux-thunk (0) | 2022.05.10 |
---|---|
[220503] redux ) handleActions, createStore (0) | 2022.05.09 |
[220502] styled-component 사용하기 (0) | 2022.05.08 |
[220429] useContext + ContextAPI 로 전역상태 관리하기 (0) | 2022.05.06 |
[220428] useReducer (0) | 2022.05.06 |
댓글