npm install @reduxjs/toolkit
이 안에 이미 react-redux가 포함되어 있기 때문에 기존에 redux패키지가 있다면 삭제해도 좋다.
이거 삭제해도 좋음
삭제한 후에 npm install을 다시 해주자
redux를 사용해도 얼마든지 전역으로 변수를 관리할 수 있다. 하지만 대형 프로젝트로 갈 수록 관리해야할 전역 변수가 많아지고, 그만큼 많은 action을 관리해야한다는 것인데, 이것을 좀더 쉽게 해주는것이 redux tookit이다.
또 react의 공식 라이브러리이기 때문에 react와 next.js에서 편리하게 사용할 수 있다.
출처 : https://www.tutorialspoint.com/redux/redux_middleware.htm
처음에는 무슨말인지 몰라도 된다. 코드를 통해 살펴보자
import { createSlice, configureStore } from "@reduxjs/toolkit";
const initialCounterState = {
counter: 0,
showCounter: true,
isAuthenticated: false,
};
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;
},
},
});
const store = configureStore({
reducer: { counter: counterSlice.reducer, auth: authSlice.reducer }, //이러면 슬라이스를 여러개 넣을 수 있다.
});
export const counterAction = counterSlice.actions;
export default store;
전체적인 코드의 모습이다 하나하나살펴보자
import { createSlice, configureStore } from "@reduxjs/toolkit";
createSlice
: redux에서는 Reducer라는 함수를 따로 정의해 주었는데 redux tookit에서는 createSlice라는 reducer를 편리하게 생성할 수 있는 함수를 제공한다.configureStore
: redux에서는 createStore를 사용했다면 redux tookit에서는 configureStore 로 내가 관리하고 싶은 전역변수를 구역별로 나눈다.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;
},
},
});
createSlice로 기존에 redux에서 만들었던 reducer를 생성할 수 있다.
name
: Redux DevTools에서 slice를 식별하는데 사용된다. 실제 코드에서는 사용되지 않고 개발자가 개발자 도구에서 slice를 쉽게 구별해 줄 수 있도록 하는 역할을 한다.initialState
: 관리해줄 전역변수의 초기값을 삽입한다.reducer
: 전역변수의 상태를 어떻게 관리할지 정의한다.전역변수는 전역변수 자체를 변경하면 안되다고 redux를 사용할 때 배웠을 것이다. 여기서는 state.countrer++
와 같이 state에서 직접 값을 변경하는데 그 이유는 내부적으로 값을 복사하는 immer이라는 함수가 존재하기 때문에 보이는 코드처럼 직접 값을 수정해도 아무 문제가 없다.
또 변경할 값을 임의로 넣고 싶다면 action에 정의되어있는 payload의 값을 사용하면 되는데 이건 실제로 redux를 컴 포넌트에서 사용할 때 값이 어떻게 넘어오나 확인할 것이다.
//slice를 한개만 넣고 싶을 때
const store = configureStore({
reducer:counterSlice.reducer
});
//slice를 여러개 넣고 싶을 때
const store = configureStore({
reducer: { counter: counterSlice.reducer, other: otherSlice.reducer }, //이러면 슬라이스를 여러개 넣을 수 있다.
});
configureStore
: 기존에 사용했던 createStore 대신에 configureStore에 reudcer를 등록한다 좋은점은 여러개의 slice를 동시에 등록하고 관리할 수 있다는 점이다.
export const counterAction = counterSlice.actions;
export default store;
외부에서 해당 reducer를 사용할 수 있도록 export 해주자 counterAction으로 import되는 counterSlice.actions는 reducer를 조작할 수 있는 함수를 갖고 있다.
실제 코드를 통해 어떻게 컴포넌트에 적용되는지 확인해 보자
function App() {
return (
<>
<Counter />
</>
);
}
단순한 동작화면만 보여주기 위해 Counter 만 배치해 두었다
import classes from "./Counter.module.css";
import { useSelector, useDispatch } from "react-redux";
import { counterAction } from "../store";
const Counter = () => {
const counter = useSelector((state) => state.counter);
const dispatch = useDispatch();
const incrementHandler = () => {
dispatch(counterAction.increment());
};
const decrementHandler = () => {
dispatch(counterAction.decrement());
};
const increaseHandler = () =>{
dispatch(counterAction.increase(10))
}
const toggleCounterHandler=()=>{
console.log(counter.showCounter)
dispatch(counterAction.toggleCounter())
}
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
{counter.showCounter && <div className={classes.value}>{counter.counter}asdf</div>}
<div>
<button onClick={incrementHandler}>increase</button>
<button onClick={increaseHandler}>increase by 5</button>
<button onClick={decrementHandler}>decrement</button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
export default Counter;
해당 코드는 다음과 같은 화면을 의미한다.
increase
: 1씩 증가한다.increase by 5
: 5씩 증가한다.decrement
: 1씩 감소한다.Toggle Counter
: 숫자가 보이지 않게 한다.이제 코드를 하나씩 살펴보자
import { useSelector, useDispatch } from "react-redux";
import { counterAction } from "../store";
ukseSelector
: configureStore에 등록된 state를 알아서 찾아주는 아주 고마운 hook이다.useDispatch
: 이 함수에 action을 전달하여 state를 변경한다. const counter = useSelector((state) => state.counter);
const dispatch = useDispatch();
counter
: 우리는 configureStore에 counter: counterSlice.reducer
라는 리듀서를 넣은 기억이 있을 것이다. 여기서 말하는 state.counter란 counterSlice에 있는 초기값을 말하는 것이다. 만약 state.counter가 아닌 state.other 을 사용했다면 otherSlice의 초기값을 const counter에 담았을 것이다.dispatch
: 이함수에 action을 담아 전달한다.
const incrementHandler = () => {
dispatch(counterAction.increment());
};
const decrementHandler = () => {
dispatch(counterAction.decrement());
};
const increaseHandler = () =>{
dispatch(counterAction.increase(10))
}
const toggleCounterHandler=()=>{
console.log(counter.showCounter)
dispatch(counterAction.toggleCounter())
}
dispatch
로 액션을 전달하기 위해서는 위에서 import
한 counterAction
을 사용한다. counterAction
은 counterSlice.actions
에서 나온것으로 reducer
를 조작할 수 있는 함수들이 담겨있다.
즉 counterAction.incrememt()를 동작하게 되면 자동으로 action객체가 생성되어 dispatch에 전달하게 된다.
const increaseHandler = () =>{
dispatch(counterAction.increase(10))
}
payload
: 함수내부에 값을 전달하면 { type : SOME_UNIQUE_TYPE, payload : 10 }
다음과 같은 action객체가 전달된다. payload는 reducer에서 사용할 수 있다.
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
{counter.showCounter && <div className={classes.value}>{counter.counter}</div>}
<div>
<button onClick={incrementHandler}>increase</button>
<button onClick={increaseHandler}>increase by 5</button>
<button onClick={decrementHandler}>decrement</button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
/이제 전역변수를 컴포넌트에 마음것 사용해도 된다!
recoil만 사용해서 프로젝트를 진행했었는데 왜 redux를 사용하는지 알것같은 느낌이든다 . 혹시라도 틀린점이 있다면 댓글로 알려주세요!