반응형
Redux Toolkit
Redux가 공식적으로 만든 라이브러리로, Redux를 쉽게 사용할 수 있게 도와준다.
# Redux Toolkit 설치 방법
// Redux + Plain JS template
npx create-react-app my-app --template redux
// Redux + TypeScript template
npx create-react-app my-app --template redux-typescript
// 기존 프로젝트에서 시작하기
npm install @reduxjs/toolkit
configureStore (createStore → configureStore)
reducer를 전달할 때는 reducer 라는 property name으로 전달해야 한다. slice에서 만든 reduce 함수를 가져와서 store에 추가한다.
// slice 1개 일 때
const store = configureStore({reducer: counterSlice.reducer })
slice가 여러 개라면 reducer의 값을 object로 하여 여러 reducer를 병합하는 방식을 사용한다(combineReducers 사용 x).
//slice 여러 개 일 때
const store = configureStore({reducer: { counter: counterSlice.reducer , auth: authSlice.reducer} })
createSlice (reducer + action → createSlice)
createSlice를 통해 Action과 Reducer를 한 번에 정의할 수 있다. Redux Toolkit은 immer 라이브러리를 내장하고 있어 기존 상태를 자동으로 복제하기 때문에 실수로라도 기존 상태 값을 직접 조작할 수 없다. 따라서 기존의 Redux 사용 방식과 달리 상태 값을 직접 변경하는 방식으로 코드를 작성해 재정의할 수 있다.
slice를 생성하려면 slice를 식별하기 위한 name, 초기 상태 값 initialState, 상태 업데이트 방법을 정의하는 하나 이상의 reducer 함수가 필요하다.
const counterSlice = createSlice({name, initialState, reducers})
slice가 생성되면 생성된 Redux 액션 생성자와 전체 slice에 대한 reducer 기능을 export 한다.
counterSlice.actions : useDispatch 훅을 이용해 dispatch할 수 있다(컴포넌트에서 상태 변경 시 사용). counterSlice.reducer : useSelector 훅을 이용해 저장소에서 전역 상태를 읽을 수 있다(store에서 reducer 함수들 merge 시 사용).
[예시]
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counter';
import authReducer from './auth';
const store = configureStore({
reducer: {
counter: counterReducer,
auth: authReducer,
},
});
export default store;
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store/index';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// store/counter.js
import { createSlice } from '@reduxjs/toolkit';
const initialCounterState = { counter: 0, showCounter: true };
const counterSlice = createSlice({
name: 'counter',
initialState: initialCounterState,
reducers: {
increment(state) {
state.counter++;
},
decrement(state) {
state.counter--;
},
increase(state, action) {
state.counter = state.counter + action.payload;
},
toggleCounter(state) {
state.showCounter = !state.showCounter;
},
},
});
export const counterActions = counterSlice.actions;
export default counterSlice.reducer;
// store.auth.js
import { createSlice } from '@reduxjs/toolkit';
const initialAuthState = {
isAuthenticated: false,
};
const authSlice = createSlice({
name: 'authentication',
initialState: initialAuthState,
reducers: {
login(state) {
state.isAuthenticated = true;
},
logout(state) {
state.isAuthenticated = false;
},
},
});
export const authActions = authSlice.actions;
export default authSlice.reducer;
// Counter.js
import { useSelector, useDispatch } from 'react-redux';
import { counterActions } from '../store/counter';
const Counter = () => {
const dispatch = useDispatch();
const counter = useSelector(state => state.counter.counter);
const show = useSelector(state => state.counter.showCounter);
const incrementHandler = () => {
dispatch(counterActions.increment());
};
const increaseHandler = () => {
dispatch(counterActions.increase(10));
};
const decrementHandler = () => {
dispatch(counterActions.decrement());
};
const toggleCounterHandler = () => {
dispatch(counterActions.toggleCounter());
};
return (
<div>
<h1>Redux Counter</h1>
{show && <div>{counter}</div>}
<div>
<button onClick={incrementHandler}>Increment</button>
<button onClick={increaseHandler}>Increment by 10</button>
<button onClick={decrementHandler}>Decrement</button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</div>
);
};
export default Counter;
// Auth.js
import { useDispatch } from 'react-redux';
import classes from './Auth.module.css';
import { authActions } from '../store/auth';
const Auth = () => {
const dispatch = useDispatch();
const loginHandler = (event) => {
event.preventDefault();
dispatch(authActions.login());
};
return (
<main className={classes.auth}>
<section>
<form onSubmit={loginHandler}>
<div className={classes.control}>
<label htmlFor='email'>Email</label>
<input type='email' id='email' />
</div>
<div className={classes.control}>
<label htmlFor='password'>Password</label>
<input type='password' id='password' />
</div>
<button>Login</button>
</form>
</section>
</main>
);
};
export default Auth;
// header.js
import { useSelector, useDispatch } from 'react-redux';
import classes from './Header.module.css';
import { authActions } from '../store/auth';
const Header = () => {
const dispatch = useDispatch();
const isAuth = useSelector((state) => state.auth.isAuthenticated);
const logoutHandler = () => {
dispatch(authActions.logout());
};
return (
<header className={classes.header}>
<h1>Redux Auth</h1>
{isAuth && (
<nav>
<ul>
<li>
<a href='/'>My Products</a>
</li>
<li>
<a href='/'>My Sales</a>
</li>
<li>
<button onClick={logoutHandler}>Logout</button>
</li>
</ul>
</nav>
)}
</header>
);
};
export default Header;
반응형
'온라인 강의(유데미, 인프런 등) > React 완벽 가이드(유데미)' 카테고리의 다른 글
[react & Recoil] Recoil atom, selector 개념 간단하게 살펴보기 (0) | 2023.03.06 |
---|---|
[react & RTK] React Toolkit 비동기 작업 처리하기(createAsyncThunk) (0) | 2023.03.04 |
[react & Redux] Redux 개념 및 예시 간단하게 알아보기 (0) | 2023.03.03 |
[react] HTTP 상태 코드 및 예외처리 (0) | 2023.03.03 |
[react] 최적화 - React.memo() & useMemo() & useCallback() (0) | 2023.03.02 |