1. install 및 디렉토리 구조
npm install react-redux
npm install @reduxjs/toolkit

1) Store
configureStore 함수는 리덕스 라이브러리의 createStore 함수를 추상화한 것이다. 기존의 번거로웠던 리덕스 설정을 간편하게 할 수 있도록 해주고 설정시 디폴트로 redux-thunk와 DevTools를 제공한다.
import { configureStore } from '@reduxjs/toolkit';
import counterSlice from '../slice/counterSlice';
import authSlice from '../slice/authSlice';
const store = configureStore({
reducer: {
counterSlice: counterSlice.reducer,
authSlice: authSlice.reducer
}
});
export default store;
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import store from './redux/store/store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
2) Slice
2-1. createSlice
createSlice 함수는 선언한 slice의 name에 따라서 actionCreator, actionType, Reducer를 자동으로 생성해준다. 따라서 별도로 createAction이나 createReducer를 사용하지 않아도 된다.
import { createSlice } from "@reduxjs/toolkit";
const initialCounterState = { countNum: 0, isCountNum: true };
const counterSlice = createSlice({
name: 'counter',
initialState: initialCounterState,
reducers: {
increaseByOne(state) {
state.countNum++;
},
decreaseByOne(state) {
state.countNum--;
},
increaseByFive(state, action) {
state.countNum = state.countNum + action.payload;
},
toggle(state) {
state.isCountNum = !state.isCountNum;
}
}
})
export const { increaseByOne, decreaseByOne, increaseByFive, toggle } = counterSlice.actions;
export default counterSlice;
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 { login, logout } = authSlice.actions;
export default authSlice;
3) Counter component
import { useDispatch, useSelector } from 'react-redux';
import { increaseByOne, decreaseByOne, increaseByFive, toggle } from '../redux/slice/counterSlice';
function Counter() {
const dispatch = useDispatch();
const counterState = useSelector((state) => state.counterSlice);
const incrementHandler = () => {
dispatch(increaseByOne());
}
const increaseHandler = () => {
dispatch(increaseByFive(5));
}
const decrementHandler = () => {
dispatch(decreaseByOne());
}
const toggleCounterHandler = () => {
dispatch(toggle());
};
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
{counterState.isCountNum ? <div className={classes.value}>{counterState.countNum}</div> : null}
<div>
<button onClick={incrementHandler}>+1</button>
<button onClick={increaseHandler}>+5</button>
<button onClick={decrementHandler}>-1</button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
export default Counter;
4) Auth component
import { useDispatch } from 'react-redux';
import { login } from '../redux/slice/authSlice';
function Auth() {
const dispatch = useDispatch();
const loginHandler = (e) => {
e.preventDefault();
dispatch(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;
import { useDispatch, useSelector } from 'react-redux';
import { logout } from '../redux/slice/authSlice';
function Header() {
const dispatch = useDispatch();
const authState = useSelector((state) => state.authSlice.isAuthenticated);
const logoutHandler = () => {
dispatch(logout());
}
return (
<header className={classes.header}>
<h1>Redux Auth</h1>
{authState ?
<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>
: null}
</header>
);
};
export default Header;