Redux is a pattern and library for managing and updating application state, using events called "actions".
→ 직역하자면, 리덕스는 action이라는 이벤트를 통해 state를 관리 및 업데이트하는 라이브러리이자 패턴이다.
The patterns and tools provided by Redux make it easier to understand when, where, why, and how the state in your application is being updated, and how your application logic will behave when those changes occur.
global state을 관리하기 편함 → 전역상태라는 것에 유의하자!!Redux is more useful when:
- You have large amounts of application state that are needed in many places in the app
- The app state is updated frequently over time
- The logic to update that state may be complex
- The app has a medium or large-sized codebase, and might be worked on by many people
Not all apps need Redux. Take some time to think about the kind of app you're building, and decide what tools would be best to help solve the problems you're working on.
→ 무조건적으로 redux를 사용해야하는 것은 아니다. redux를 사용하면 코드가 많아지고 (boilerplate code가 많음) redux에 관한 지식도 배워야하기 때문에 기회비용이 있다. 하지만 규모가 큰 프로젝트라면 redux를 사용한 state management를 고려해보는 것도 좋다.
state → view → action → state
State – app에서 사용되는 current state
View – UI. 사용자한테 보여지는 화면
Actions – state를 변경하는 event
다음과 같은 과정이 계속 반복되며 이를 one-way-data-flow라고 한다.
type : 발생한 이벤트 이름, 필수 → 주로 "domain(state)/eventName” 형식으로 작성
payload : 이벤트에 대한 추가 정보 (추가 / 수정 / 삭제 데이터 등..), 선택
const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
action creator은 필수는 아니지만 효율성을 증대시켜준다.
- 반복된 action객체를 손수 작성하는 것보다 함수(action creator)를 호출하는 것이 간단하다.
- type에 들어가는 string을 잘못 작성하거나 type,payload property를 잘못 작성하는 실수를 줄일 수 있다.
const addTodo = text => {
return {
type: 'todos/todoAdded',
payload: text
}
}
(state, action) ⇒ newState
3가지 원칙을 반드시 준수해야 한다.
state와 action을 통해서만 newState를 도출해야 한다.
현재 state를 직접 수정하는 것이 아닌 복사본을 만들어서 복사본을 수정해야 한다.
→ immutable update !! (array, object의 경우 spread operator를 사용한다)
asynchronous (비동기), 랜덤 value 생성, 다른 side effect를 발생시키는 로직은 금지
주로 switch 구문으로 작성.
→ 하지만 이것은 필수가 아니다. if/else, loop 등 원하는 것을 사용하여 구현 가능하다.
const initialState = { value: 0 }
function counterReducer(state = initialState, action) {
// Check to see if the reducer cares about this action
switch(action.type) {
case 'counter/increment' :
// If so, make a copy of `state`
return {
...state,
// and update the copy with the new value
value: state.value + 1
}
default:
// otherwise return the existing state unchanged
return state
}
}
store : global state를 보관하고 관리하는 저장소. 앱에는 하나의 store만 존재해야 한다.
dispatch : 발생한 action 객체를 store로 보내는 역할을 하는 api.
state를 업데이트할 수 있는 유일한 방법이다.
reducer을 통해 반환된 newState는 다시 store에 보관된다.
action creator가 있는 경우 action을 대신해서 사용한다.
// action creator가 없는 경우
store.dispatch({ type: 'counter/increment' })
// action creator가 없는 경우
store.dispatch({ type: 'counter/increment' })
// action creator가 있는 경우
const increment = () => {
return {
type: 'counter/increment'
}
}
store.dispatch(increment())
앱의 규모가 커지고 관리하는 state가 많아졌을 때 유용.
const selectCounterValue = state => state.value
const store = createStore(countUpReducer);
store.getState();
const actionObj = {type: 'count/increment'}
const actionCreatorFunc = () => {type: 'count/increment'}
store.dispatch(actionObj);
store.dispatch(actionCreatorFunc());
reducer(store.getState(), actionObj)가 자동으로 실행됨.
const printCurrentState = () => {
const state = store.getState()
console.log(`state: ${state}`);
}
const unSubscribe = store.subscribe(printCurrentState);
unSubscribe()
slice = 각각의 state
sliceReducer
combineReducer
createStore의 인자로 reducer을 넣어줘야 하는데 sliceReducer이 생기면서 여러개의 reducer로 쪼개졌다. 이를 하나로 합쳐주는 것이 combineReducer이다.
combineReducer의 인자에는 slice에 해당하는 sliceReducer이 정의된 객체이며 rootReducer을 반환한다.
const rootReducer = combineReducers({
todos: todosReducer,
filter: filterReducer
})
const store = createStore(rootReducer);
src/
|-- index.js
|-- app/
|-- App.js (+)
|-- store.js
|-- components/
|-- FavoriteButton.js (+)
|-- Recipe.js (+)
|-- features/
|-- allRecipes/
|-- AllRecipes.js (+)
|-- allRecipesSlice.js
|-- favoriteRecipes/
|-- FavoriteRecipes.js (+)
|-- favoriteRecipesSlice.js
|-- searchTerm/
|-- SearchTerm.js (+)
|-- searchTermSlice.js
https://codesandbox.io/embed/redux-vanilla-js-q6flod?fontsize=14&hidenavigation=1&theme=dark
https://codesandbox.io/embed/react-redux-counter-4f51t6?fontsize=14&hidenavigation=1&theme=dark