이전 게시글과 같은 기능의 redux로 상태관리하는 것인데, 폴더구조를 다르게 해보았다.
모듈
: Ducks 패턴을 사용하여 액션 타입, 액션 생성 함수, 리듀서를 작성한 코드
const INCREASE = 'counter/INCREASE'
const DECREASE = 'counter/DECREASE'
// 액션 타입 정의
액션 타입 정의
: 모듈 이름/액션이름
2. 액션 생성 함수 만들기
export const increse = () => ({ type: INCREASE })
export const decrese = () => ({ type: DECREASE })
// 액션 생성 함수 만들기
const initialState = {
number: 0
}
// 초기 상태에 number를 0으로 해줌
function counter(state = initialState, action) {
switch (action.type) {
case INCREASE:
return {
number: state.number + 1
}
case DECREASE:
return {
number: state.number - 1
}
default:
return state
}
}
export default counter
// 리듀서 함수는 현재 상태를 참조하여 새로운 객체를 생성해서 반환 하는 코드를 만듬
const SIGN_IN = 'isLogged/SIGN_IN'
// 액션 타입 정의
export const sign_in = () => ({ type: SIGN_IN })
// 액션 생성 함수 만들기
// 초기 상태
const initialState = {
sign: false
}
// 리듀서 함수
const isLogged = (state = initialState.sign, action) => {
switch (action.type) {
case 'SIGN_IN':
return !state;
default:
return state;
}
}
export default isLogged;
여러개의 리듀서를 하나로 합치는 것
import { combineReducers } from "redux";
import counter from "./counter";
import isLogged from "./isLogged";
const rootReducer = combineReducers({
counter, isLogged
})
export default rootReducer
combineReducers
이라는 유틸 함수를 이용해서 하나로 쉽게 처리 가능
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { createStore } from 'redux';
import rootReducer from './modules';
import { Provider } from 'react-redux';
const store = createStore(rootReducer)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
리액트에서 store를 사용할 수 있도록 react-redux
에서 제공하는 Provider
로 감싼다.
이 컴포넌트를 사용할 때는 store
를 props
로 전달해 주어야한다.
🧷tip
chrome - redux devTolls 설치
npm install redux-devtools-extension
설치 후, index.js에 import 해준다.
import { devToolsEnhancer } from 'redux-devtools-extension';
const store = createStore(rootReducer, devToolsEnhancer())
컨테이너 컴포넌트
: 리덕스 스토어와 연동된 컴포넌트를 컨테이너
컴포넌트에서 리덕스 스토어에 접근하여 원한느 상태를 받아 오고, 액션도 디스패치에 줄 차례
import Counter from "../components/Counter"
const CounterContainer = ({ number, increse, decrese }) => {
return <Counter />
};
export default CounterContainer;
리덕스와 연동하려면 react-redux에서 제공하는 connect 함수와 함께 사용해야 한다.
connect(mapStateToProps, mapDispatchToProps)(연동할 컴포넌트)
mapStateToProps
: 리덕스 스토어 안에 상태를 컴포너트의 props로 넘겨주기 위해 설정하는 함수
mapDispatchToProps
: 액션 생성 함수를 컴포넌트의 props로 넘겨주기 위해 사용하는 함수
예를들면,
const makeContainer = connect(mapStateToProps,mapDispatchToProps)makeContainer(타깃컴포넌트)
import { connect } from "react-redux"
import Counter from "../components/Counter"
import { increse, decrese } from "../modules/counter"
const CounterContainer = ({ number, increse, decrese }) => {
return <Counter number={number} onIncrese={increse} onDecrese={decrese} />
}
const mapStateToProps = state => ({
//state를 파라미터로 받아오고, 이 값은 현재 스토어가 지니고 있는 상태
number: state.counter.number
})
const mapDispatchToProps = dispatch => ({
// store의 내장 함수 dispatch를 파라미터로 받아온다.
increse: () => {
dispatch(increse())
// 액션 생성 함수를 불러와서 액션객체를 만들고 디스패치
},
decrese: () => {
dispatch(decrese())
}
})
export default connect(mapStateToProps, mapDispatchToProps,)(CounterContainer)
export default connect(
state => ({
number : state.counter.number,
}),
dispatch =>
bindActionCreators(
{
increse,
decrese,
},
dispatch,
),
)(CounterContainer)
import CounterContainer from "./containers/CounterContainer";
import IsLogged from "./components/IsLoggde";
function App() {
return (
<>
<CounterContainer />
<IsLogged />
</>
);
}
export default App;
리덕스를 훨씬 더 편하게 사용할 수 있다.
npm install redux-actions
switch/case
문이 아닌 handleActions
라는 함수를 사용해 각 액션마다 업데이트 함수를 설정하는 형식import { createAction, handleActions } from "redux-actions"
const INCREASE = 'counter/INCREASE'
const DECREASE = 'counter/DECREASE'
// 액션 타입 정의
export const increse = createAction(INCREASE)
export const decrese = createAction(DECREASE)
// 액션 생성 함수 만들기
const initialState = {
number: 0
}
// 초기 상태에 number를 0으로 해줌
const counter = handleActions(
{
[INCREASE]: (state, action) => ({ number: state.number + 1 }),
[DECREASE]: (state, action) => ({ number: state.number - 1 }),
},
initialState,
)
export default counter
useSelector
를 사용하여 connect 함수를 사용하지 않고 리덕스의 상태를 조회할 수 있다.
const 결과 = useSelector(상태 선택 함수)
useDispatch
: 컴포넌트 내부에서 스토어의 내장함수 dispatch를 사용 할 수 있게 해줌
const dispatch = useDispatch();
dispatch({ type : 'SAMPLE_ACTION'});
useCallback
으로 액션을 디스ㅐ치 하는 함수를 감싸주어, 컴포너느 성능을 최적화한다.
import { useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import Counter from "../components/Counter";
import { increse, decrese } from "../modules/counter";
const CounterContainer = () => {
const number = useSelector((state) => state.counter.number);
const dispatch = useDispatch();
const onIncrease = useCallback(() => dispatch(increse()), [dispatch]);
const onDecrease = useCallback(() => dispatch(decrese()), [dispatch]);
return (
<Counter number={number} onIncrease={onIncrease} onDecrease={onDecrease} />
);
};
export default CounterContainer;
작은 프로젝트에서 리덕스를 적용하면 오히려 프로젝트의 복잡도가 높아질 수 있다.
하지만, 규모가 큰 프로젝트에 리덕스를 적용하면 상태를 더 체계적으로 관리할 수 있다!
🔍 참고자료 : 리액트를 다루는 기술
💬 다양한 방법으로 redux를 학습하였는데, 실무에서는 어떤 방법으로 쓰는 지 궁금하다.
프로젝트에 적용해 보지 않아서 적용해 보고 싶다!
아무래도 동영상은 영어로 되어있어서, 책으로 본 게 설명을 자세히 들을 수 있었다.