리덕스 부터 시작을 해서 toolkit까지 사용해봤는데,,ㅎㅎ
결국엔 toolkit으로 기계처럼 사용하고 있네요ㅎ
카운터가 가장 보편화된 예시인데, 실제로 사용하면서 비동기처리를 필요했던 부분이 있는데 내용은 차후에 다시 정리해봐야겠다
npm install redux
npm install react-redux
상태 관리를 위해서 Redux를 사용하려고 하는것!
useState를 사용할 때 setState를 이용하여 값을 변경하는 것처럼 모든 파일에서 ( = 전역에서) 접근할 수 있는 state가 있고 그걸 원하는 값으로 변경하는 기능만 있으면 된다.
Redux 기본 용어
Store
Action
Reducer
Dispatch
Subscribe
위에서 설명한 3가지 이름으로 파일을 만들어 사용한다.
파일명은 자유지만 보통 이렇게 사용한다.
import { createStore } from "redux";
import rootReducer from "./rootReducer";
const store = createStore(rootReducer);
export default store;
store.js에서는 다음과 같은 코드로 store를 생성해준다.
createStore는 첫번째 인자로 reducer를 받는다.
여기서는 rootReducer이다.
const initialState = {
data: [],
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case "setData":
return { ...state, data: action.payload };
default:
return state;
}
};
export default rootReducer;
rootReducer에는 state와 action이 들어간다.
state에는 초기값을 지정해줄 수 있다.
이 경우에 action을 setState와 같은 기능을 하는 setData를 만들어주었다.
기존에 state 객체를 복사해와서 그안에 data라는 key와 action에서 받아오는 payload를 value로 state를 바꿔주는 역할을 한다.
export const setData = data => ({
type: "setData",
payload: perData,
});
setData는 perData라는 새로 변경해줄 데이터를 받아서 reducer에 전달해주는 action이다.
index.jsx
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux";
import store from "redux/store";
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById("root")
);
import { connect } from "react-redux";
const mapStateToProps = state => {
return {
reduxData: state.data,
};
};
const Page = ({ reduxData }) => {
...
return (
...
);
};
export default connect(mapStateToProps)(Page);
컴포넌트에서 store에 있는 state(이 경우는 reduxData)를 사용하려면 connect함수를 이용하여 mapStateToProps와 Page를 연결해줘야 한다. 연결하면 해당 컴포넌트에서 props.reduxData로 state를 사용할 수 있게 된다.
import React, { useState, useEffect } from "react";
import { getSearchData } from "api";
import { Button } from "components";
import { connect } from "react-redux";
import { setData } from "redux/actions";
const mapDispatchToProps = dispatch => {
return {
setData: data => dispatch(setData(data)),
};
};
const StateEdit = ({ setData }) => {
const searchHandler = async () => {
if (targetData) {
const data = await getSearchData(targetData);
console.log(data);
setData(data.data);
}
};
return (
<>
<div className="button" onClick={() => searchHandler()}>
<span className="text">출력</span>
</Button>
</>
);
};
export default connect(null, mapDispatchToProps)(StateEdit);
npm install @reduxjs/toolkit
Provider는 react-redux에서 리액트 앱에 스토어를 연동할 수 있게 해주는 컴포넌트다. 아래와 같이 Provider 컴포넌트를 불러와 연동할 컴포넌트를 감싸준 뒤, Provider의 props로 사용할 스토어를 지정해주면 된다.
// App.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { Provider } from "react-redux";
import store from "./store/store.js";
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
)
먼저 store.js에 configureStore()로 스토어를 만들어주자.
// store.js
import { configureStore } from '@reduxjs/toolkit';
export const store = configureStore({
reducer: counterSlice,
middleware: [...middlewares]
})
기존 리덕스에서는 스토어 생성 후 미들웨어가 한 개 이상이라면, 여러 메서드를 통해 긴 코드를 작성해야 했다.
configureStore()는 별도의 메서드 없이 바로 미들웨어를 추가할 수 있다는 장점이 있다.
기존 리덕스에서는 액션을 디스패치하기 위한 별도의 함수가 필요했고, 액션의 객체를 리듀서를 통해 리턴하는 구조였다.
createSlice()는 액션에 대한 함수 설정과 리듀서를 따로 생성하지 않아도 된다.
아래는 createSlice()를 사용한 카운트업 코드와 그에 대한 설명이다.
// AllCheck.jsx
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
plus: state => {
state.value += 1
},
minus: state => {
state.value -= 1
},
},
})
export const { plus, minus } = counterSlice.actions;
export default counterSlice.reducer;
useSelector()는 기존 리덕스의 connect()를 이용하지 않고 리덕스의 상태를 조회할 수 있다.
useDispatch()는 생성한 액션을 발생시키며, 액션생성 함수를 가져온다. 위 설명의 디스패치와 같다고 보면 된다.
아래는 useSelector()와 useDispatch()에 대한 설명이다.
import React from 'react'
import { useDispatch, useSelector } from 'react-redux';
import { plus, minus } from './counter/countSlice01';
export default function App() {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<button onClick={() => dispatch(minus())}>-</button>
Value: { count }
<button onClick={() => dispatch(plus())}>+</button>
</div>
);
}
useSelector()로 스토어에서 현재 상태 값을 가져온다.
useDispatch()를 통해 변경되는 값을 스토어로 전달한다.