store: 전역 상태 모음 저장소 (state, reducer, 내장 함수들이 들어있음)action: 상태에 변화를 주기 위해 필요한 도구 객체type은 action 객체의 필수 keyreducer(): state, action을 파라미터로 받아 state 값을 변경하는 함수dispatch(): action을 파라미터로 받아 Store가 Reducer를 실행하도록 함initialState 생성, action 객체 생성
-> someReducer = state, action을 받는 reducer
-> rootReducer = combineReducers({ someReducer, ...리듀서들 객체 })
-> store = createStore(rootReducer)
useSelector() : store의 state에 접근 가능 = store.getState()
ex. useSelector(state => state.{ 리듀서 이름(someReducer) })useDispatch() : dispatch 불러오기 = store.dispatch()const dispatch = useDispatch();용어나 동작 원리 자체는 Context API와 useReducer를 함께 사용하는 것과 크게 다르지 않은 것 같다.
그렇다면 Redux는 왜 사용하는 걸끼?
store에 모두 모아 사용한다는 점 (combineReducers로 리듀서들을 모은 rootReducer를 store에 저장함) -> 하나의 store에 모두 넣을 수 있어 Context 분리/관리를 신경쓰지 않아도 됨Provider 하위의 컴포넌트가 모두 리렌더링 되지 않고 실제 상태가 변경된 부분만 리렌더링redux-thunk, redux-logger 등Redux-Tool-Kit은 미들웨어가 아닌, 리덕스를 보다 잘(?) 사용할 수 있도록 도와주는 도구 모음이다.
리덕스를 사용할 때 유용한 도구들을 붙인 커스텀 훅을 제공하는, 마치 갑옷을 입히고 더 좋은 무기를 들게 한 리덕스라고 생각한다.
공식 문서에서 본 중 기억에 남는건,
immer를 자동으로 적용하므로 간단한 형태로 상태 불변성을 지킬 수 있음그 외의 기능은 학습하며 이해할 수 있을만한 문장..
createSlice()slice는 뭘까? action과 reducer, initialState를 한데 합쳐놓은 예쁜 리듀서 세트 정도다.
createSlice()에 name, initialState, reducers를 포함하는 객체를 파라미터로 주어 slice를 생성
import { createSlice } from '@reduxjs/toolkit';
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: state => {
state.value += 1;
},
decrement: state => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
slice의 name은 action.type의 도메인 부분이기도 하다.
-> sliceName/reducerName 형식으로 action의 type이 정해짐.
(ex. action의 type: counter/increment, counter/decrement, …)
reducer에 switch문 대신 객체 형태가 사용된다.
위의 설명처럼 sliceName/reducerName이 switch문의 action.type을 대체하며, key 값 자체만으로 reducer 명이다.
자동으로 액션 생성자(action creator)를 만들어 준다.
마지막의 export를 보면 counterSlice.actions로부터 내보내기를 하는 것이 보이는데, 이들은 리듀서나 액션 객체가 아닌 모두 액션 생성자다.
이 때의 액션 생성자 이름은 각각의 액션과 연결된 리듀서명과 같다.
다른곳에서 import하여 increment()와 같이 액션 객체를 생성하여 사용할 수 있음
export default된 slice의 reducer로 store를 생성할 수 있다.
createSlice()를 통해 생성된 slice에서는 immer를 내장하고 있으므로 내부에서 위험해보이는(?) mutating을 사용할 수 있다.
(ex. state => state.value += 1;)
configureStore()예쁜 리듀서 세트인 slice들을 모아 하나의 전역 관리 저장소인 store를 생성한다.
import { configureStore } from '@reduxjs/toolkit';
import usersReducer from '../features/users/usersSlice';
import postsReducer from '../features/posts/postsSlice';
import commentsReducer from '../features/comments/commentsSlice';
export default configureStore({
reducer: {
users: usersReducer,
posts: postsReducer,
comments: commentsReducer,
},
});
store의 state에 접근하게 되면, 이 리듀서 집합에 닿게 된다.
따라서 각각의 slice에는,
state.users, state.posts, state.comments와 같이 접근하면 된다.
useSelector()방법이 두 가지가 있는 것 같다.
useSelector()를 이용하는 방법과 store에 접근하는 방법,
useSelector()
useSelector()의 리턴값은 state다.
따라서 이 안에 원하는 slice의 state를 추출하는 커스텀 함수를 넣으면, 특정 slice의 state를 받아올 수 있다.
export const selectCount = state => state.counter;
const count = useSelector(selectCount);
// { value : 0 }
store 접근
export const selectCount = state => state.counter;
// 또는 store.getState().counter;
const count = selectCounter(store.getState());
// { value : 0 }
state에 접근하는 프로퍼티명도 slice의 name이다.
useDispatch()useSelector()와 useDispatch() 모두 리덕스와 동일하다.
store.으로 직접 접근도 가능하지만, Hook을 사용하여 state와 dispatch를 사용할 수 있다.