이전 포스팅에서 리액트에서 리덕스를 세팅했던 방식은, 하나의 리듀서를 만들고, 하나의 리듀서에서 action값에따라 모든 state를 관리했었다.
Store의 state값이 다양해지게 되고, 컴포넌트가 많아질수록, 이를 구분할 필요가 있다.
아주 간단한 예를들면.. A컴포넌트에서는 State:{counter:number, toggle:boolean} 를 구독하고
B컴포넌트(유저인증) 에서는 state:{auth:boolean} 를 구독하고있다.
이때 A,B 컴포넌트가 독립적이므로 의미상 리듀서를 2개 놓고, dispatch를 통해 소통하는 방식이 헷갈리지않고 파일을 나누기 편리하다.
npm install @reduxjs/toolkit 을 설치한다.
store/index.js (리액트 root폴더의 index.js가 아님!)
import {createSlice,configureStore} 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--;
},
toggle(state){
state.showCounter = !state.showCounter
}
}
});
//-----------------------------------------------------------------
const initialAuthState = {
isAuthenticated:false
};
const authSlice = createSlice({
name:'auth',
initialState: initialAuthState,
reducers:{
login(state){
state.isAuthenticated = true;
},
logout(state){
state.isAuthenticated = false;
}
}
});
const store = configureStore({//root reducer
reducer: {counter: counterSlice.reducer, auth:authSlice.reducer}
});
export const counterActions = counterSlice.actions;
export const authActions = authSlice.actions;
export default store;
createSlice를 사용해서, 의미상 구분된 리듀서를 만들 수 있다.
Slice객체를 만든 다음, store에 등록을 해준다.
const store = configureStore({//root reducer
reducer: {counter: counterSlice.reducer, auth:authSlice.reducer}
});
위 부분과 같이 우리가만든 slice 리듀서를 root리듀서에 매핑을 해주어야한다.
내부적으로, 여러 리듀서들을 root리듀서에 병합 해준다
만약 구독하는 컴포넌트에서 store에 접근하려면, state.counter.counter , state.auth.isAuthenticated와 같이 매핑된 리듀서 ID를 넣어주어야한다.
export const counterActions = counterSlice.actions;
export const authActions = authSlice.actions;
컴포넌트에서 dispatch를 할때에 필요하기 때문에 위처럼 Actions객체를 export 해주어야한다.
컴포넌트에서는 Actions객체는 다음과 같이 사용한다.
import { useSelector, useDispatch } from 'react-redux';
import {authActions,counterActions} from '../store/index';
const logoutHandler = () => {
dispatch(authActions.logout());
}
Actions객체에 접근한 다음, slice에 정의해둔 이름과 같은 reducer를 실행시키면,
내부적으로 그에맞는 action객체를 리턴해주고 dispatch를 통해 해당 리듀서 함수를 실행시켜준다.
만약 payload가 필요하다면 dispatch(authActions.logout(payloadValue)); 를 호출하고,
store파일에서 해당 reducer에 매개변수로 (state,action)를 받으면된다. action.payload로 값을 사용하면된다.
툴킷자체가 내부적인 동작이 많기 때문에, 설명을 상세히 달아두지않으면 까먹을 것 같아서 이렇게 까지 써둔다.
특이한 점
const counterSlice = createSlice({
name:'counter',
initialState:initialCounterState,
reducers: {
increment(state){
state.counter++;
},
decrement(state){
state.counter--;
},
toggle(state){
state.showCounter = !state.showCounter
}
}
});
이 부분이다. 상태변화를 줄때 불변성을 지키지 않고 state에 직접 접근한 다음 값 변경을 하고있다.
하지만 이는 내부적으로 state값을 clone해서(새로운 state객체 생성), 값을 변화시킨다음 override 해준다.
즉 내부적인 동작을 통해 ( immer 라이브러리를 통해 )
increment(state){
return {
...state
counter:state.counter+1;}
}
의 형태로 바꿔준다. 불변성이 지켜지고 있다.
위처럼 한 store에 모든걸 몰아넣었는데 리듀서 별로 파일로 모듈화 하는게 좋다.
분리하는것도 다음에 포스팅 해야지!
github:
https://github.com/sae1013/react-redux-demo/tree/redux-slice/src