들어가기
지난 번 글에서 Redux에 대해 간단히 알아보았다.
React에서는 Redux Store를 어떻게 만들고, 어떻게 제공하고 그 데이터를 어떻게 사용하는지 예제를 통해 알아보자.
Store 만들기
만들기 전에 먼저 yarn add(또는 npm i) redux react-redux로 라이브러리를 설치한다.
이전에 적었는지 모르겠지만 Redux는 React에서만 사용할 수 있는 라이브러리가 아니기 때문에 react-redux도 설치해야 한다.
store 폴더를 만들고 index.js를 생성한다.
꼭 폴더명을 store로 할 필요는 없지만 통상적으로 store라고 사용하니 절대다수를 따라주자.
import { createStore } from 'redux';
const counterReducer = (state = { counter: 0 }, action) => {
if (action.type === 'plus') {
return {
counter: state.counter + 1,
};
}
if (action.type === 'minus') {
return {
counter: state.counter - 1,
};
}
return state;
};
const store = createStore(counterReducer);
export default store;
store를 만들었다.
redux에서 createStore를 import하고, Reducer 함수(state와 action을 파라미터로 받는)를 만들고,
action의 type에 따른 결과를 지정한 후 store에 데이터를 조작하는 리듀서 함수가 counterReducer라는 것을 알려준다.
이번 시간에는 파일 내부에서 subscribe하지 않고 useSelector와 useDispatch로 상태를 업데이트 해보기 위해 구독을 하지 않는다.
이번 예제는 리액트 앱과 redux store를 연결 시키기 위함이기 때문이다.
그렇기에 외부에서 사용할 수 있게 store를 export한다.
Store 제공하기
redux store를 리액트 앱과 연결 하기 위해서는 앱 전체를 렌더링하는 최상위 컴포넌트를 Provider 컴포넌트로 감싸줘야 한다.
이 Provider는 react-redux에서 import할 수 있다.
이걸로 끝이 아니라, 우리의 저장소에 접근할 수 있도록 store를 알려줘야 한다.
이 때 props로 store를 전달해주면 리액트 앱 어디에서든 store에 접근할 수 있게 된다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
Redux 데이터 사용하기
이렇게 생긴 페이지에 + 버튼을 누르면 1이 증가하고, - 버튼을 누르면 1이 감소되게 하려고 한다.
이 때 useSelector를 사용해보자.
useSelector는 react-redux에서 제공하는 훅 중 하나로, '함수형 컴포넌트' 내에서 Redux store의 상태를 읽는다.
import { useSelector } from 'react-redux';
const MyComponent = () => {
const state = useSelector(state => state.myReducer); // myReducer는 Redux 스토어에 등록된 리듀서의 이름
// 상태(state)를 이용한 작업 수행
return (
// JSX 반환
);
};
주로 이렇게 단일 인자를 가지는데, 이 인자는 콜백 함수로서 Redux store의 전체 상태를 받아와 원하는 부분만 추출하는 역할을 한다.
useSelector는 Redux store의 상태를 구독하고, 해당 상태가 변할 때마다 자동으로 컴포넌트를 리렌더링 한다.
(클래스 기반 컴포넌트에서의 connect 함수와 유사한 역할을 수행한다고 함.)
공식 문서에서도 다음과 같이 useSelector 훅을 설명한다.
Allows you to extract data from the Redux store state for use in this component, using a selector function.
(선택자 함수를 사용해 이 컴포넌트에서 사용할 Redux 스토어 상태의 데이터를 추출할 수 있습니다.)
값을 가져올 수 있다면 이제 업데이트 할 수 있어야 한다.
업데이트를 위해서 useDispatch를 사용하자.
useDispatch도 라이브러리가 제공하는 훅 중 하나로, '함수형 컴포넌트' 내에서 Redux store에 액션을 dispatch하게 한다.
즉, 액션을 보내서 Redux store의 상태를 변경 시킬 때 사용한다고 이해할 수 있다.
import { useDispatch } from 'react-redux';
const MyComponent = () => {
const dispatch = useDispatch();
const myAction = () => {
dispatch({ type: 'MY_ACTION' }); // Redux 스토어에 액션을 디스패치
};
// 액션을 디스패치하여 상태 변경
return (
// JSX 반환
);
};
이런 식으로 useDispatch 함수를 호출해 dispatch 함수를 가져온다.
이전에 했던 방식과 똑같이 type 속성을 갖는 액션 객체를 보내주면 된다.
지금은 +, - 일 때 값을 변경하는 것이니 아래와 같이 코드를 짜보자.
dispatch와 counter로 useDispatch, useSelector를 호출하고, counter에는 state의 counter만 구독할 수 있도록 한다.
다음 type에 따라 다른 액션 객체를 작성해주면 되는데, + 버튼일 때는 1씩 증가하도록, - 버튼일 때는 1씩 감소하도록 한다.
import classes from './Counter.module.css';
import { useSelector, useDispatch } from 'react-redux';
const Counter = () => {
const dispatch = useDispatch();
const counter = useSelector(state => state.counter);
const plusHandler = () => {
dispatch({ type: 'plus' });
};
const minusHandler = () => {
dispatch({ type: 'minus' });
};
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
<div className={classes.value}>{counter}</div>
<div>
<button onClick={plusHandler}>+</button>
<button onClick={minusHandler}>-</button>
</div>
</main>
);
};
export default Counter;
실행 후 각 버튼을 눌러 counter를 업데이트 해보니 정상적으로 작동함을 알 수 있다.
'⚛️ React > ✨ Redux' 카테고리의 다른 글
[Redux] Redux-toolkit 알아보기 (0) | 2024.04.14 |
---|---|
[Redux] Redux의 payload와 다중 state 작업하기 (0) | 2024.04.12 |
[Redux] Redux 알아보기 (1) | 2024.03.31 |