Written by
Parkdev
on
on
[React] Context API의 단점을 해결하는 Redux
리덕스(Redux)란?
앱레벨의 상태를 관리하는 라이브러리 (app-level-state management)
- 여러 컴포넌트의 상태를 공유하기 위한 라이브러리이다.
Redux를 써보자
이를 사용하기 위해 우선 예시 컴포넌트를 하나 만들자
import { Table } from 'react-bootstrap'
function Cart() {
return (
<div>
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>상품명</th>
<th>수량</th>
<th>변경하기</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>상품1</td>
<td>3</td>
<td>버튼</td>
</tr>
<tr>
<td>1</td>
<td>상품1</td>
<td>3</td>
<td>버튼</td>
</tr>
<tr>
<td>1</td>
<td>상품1</td>
<td>3</td>
<td>버튼</td>
</tr>
</tbody>
</Table>
</div>
)
}
export default Cart
//App.js
import Cart from './pages/Cart.js'
function App () {
return (
<Routes>
<Route path="/cart" element={<Cart/>} />
</Routes>
)
}
- 여기서 만약 장바구니에 들어가는 데이터가 App, 그리고 Detail에도 사용되는 state라고 가정하면 다음과 같이 처리가필요하다
- 최상단 컴포넌트에 useState를 만들어두고
- Props로 전달해서 이걸 또 받아서
- 전달하고 또 하위 컴포넌트가 있다면 또 전달하고…
-
이렇듯 상태 관리가 복잡해진다.
- 이럴때 상태관리 라이브러리를 쓴다면 편리하게 상태를 관리할 수 있다.
- 대표적으로 Redux, MobX, Recoil, Zustand…. 등등
- 우선 최근까지 가장 많이 사용되고있는 Redux를 사용해보자.
Redux의 작동방식
- Redux를 설치하면 하나의 redux store.js를 만든다
- 그리고 이곳에 모든
state
를 저장해준다. - 이후 부터
state
를 불러오고싶다면? props로 전달할 필요없이 redux.js 에 접근하여state
를 가져오면된다.
Redux 사용전 참고해야할 사항으로 React 18버전 이상부터 정상적으로 사용이 가능하다
Redux 사용준비
- 우선 Redux를 설치하자
npm install @reduxjs/toolkit react-redux
- 상태를 저장해줄 js 파일을 만들자
// /src에 store.js 를 만들고
import { configureStore } from "@reduxjs/toolkit";
export default configureStore({
reducer: {
}
})
- Redux를 사용할 최상단 페이지에 Import 해주자
import { Provider } from 'react-redux';
import store from './store.js';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider >
</React.StrictMode>
);
- 이러면 모든 컴포넌트가 store.js에 있는 state를 사용가능하다
Redux store에 값을 넣고 사용해보자
- 우선 상태를 생성하기 위해선 아래와 같이 추가해야한다.
// store.js
import { configureStore, createSlice } from "@reduxjs/toolkit";
// cart에 사용할 state를 만들어보자 (Redux는 이를 Slice라고한다.)
let user = createSlice({ //useState()와 비슷한 역할을 한다
name: 'user',
initialState : 'kim',
}) // { user : 'kim' } 라는 오브젝트를 출력한다
let stock = createSlice({
name: 'stock',
initialState : [10, 11, 12]
})
export default configureStore({
reducer: {
user : user.reducer,
stock : stock.reducer
}
})
- 이후 이를 사용할 컴포넌트 위치에 import 해주면 된다.
import { useSelector } from 'react-redux'
function App() {
...
let a = useSelector((state)=> { return state })
...
return (
//...본문...
)
}
참고 사항
- useSelector은 함수로 구성 되어있다. (이 이유는 정확하게 알지 못하겠다. 찾아봐도 이유가 나오지 않는 것같다.)
useSelector((state)=> { return state })
- useSelector을 사용할때 넣은
state
props는 store안의 모든 state를 담고있다. 때문에 state중 일부만을 가저오려면 다음과 같이 처리하면 된다.
useSelector((state)=> { return state.name })
- 또한 JS문법을 그대로 따르고 있으므로 다음과 같이 중괄호와
return
을 생략할 수 있다.
useSelector((state)=> state.name )
-
그리고 Redux의 경우 컴포넌트가 수십개를 넘어가는 프로젝트일때 사용하는것이 코드가 더 짧다, 때문에 소형 프로젝트에서는 오히려 props를 사용하는것이 더 나을 수도 있다.
다만 확장성을 위해서 미리 준비하는것은 나쁘지 않을것 같다
-
또한 잊지 말아야할 것은 Redux는 컴포넌트 간의 공유편의성을 위해 사용하는것이지 하나의 컴포넌트 안에서만 사용하는 state는 그냥 useState를 쓰는게 베스트이다.
예제
아래의 임시데이터를 redux를 사용해 cart.js라는 컴포넌트에 테이블로 구성해보자
[
{id : 0, name : 'White and Black', count : 2},
{id : 2, name : 'Grey Yordan', count : 1}
]
풀이
// store.js
let items = createSlice({
name: 'items',
initialState: [
{id : 0, name : 'White and Black', count : 2},
{id : 2, name : 'Grey Yordan', count : 1}
]
})
export default configureStore({
reducer: {
items : items.reducer,
}
})
//Cart.js
import { useSelector } from 'react-redux'
function Cart() {
let a = useSelector((state) => state.items)
console.log(a)import { useSelector } from 'react-redux'
return (
<div>
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>상품명</th>
<th>수량</th>
<th>변경하기</th>
</tr>
</thead>
<tbody>
{
a.map((item) => {
return (
<tr>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.count}</td>
<td>버튼</td>
</tr>
)
})
}
</tbody>
</Table>
</div>
)
}