Redux Essentials, Part 1: Redux Overview and Concepts | Redux
The official Essentials tutorial for Redux: learn how to use Redux, the right way
redux.js.org
오류가 있을 수 있습니다. 공식문서와 비교하며 읽어주세요.
Actions
Action은 type field를 가지고 있는 자바스크립트 object이다. Action은 어플리케이션에서 일어나는 일을 설명하는 event라 생각하면 된다.
Type field는 "todos/todoAdded"와 같이, action을 설명가능한 스트링이어야 한다. Type 스트링은 주로 "domain/eventName"과 같은 형태로 사용하는데, domain은 feature나 카테고리를 의미하며, eventName은 어떤 상황이 일어났느냐를 의미한다.
Action object는 일어난 일에 대한 추가적인 정보를 가질 수 있다. 컨벤션에 따르면, payload에 그 정보를 넣는다.
일반적인 action object는 이러한 형태이다:
const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
Action Creators
Action creator는 action object를 만들어 반환하는 함수다. Action object를 매번 다시 작성하지 않게끔 이 함수를 사용한다:
const addTodo = text => {
return {
type: 'todos/todoAdded',
payload: text
}
}
Reducers
Reducer는 현재 state와 action obejct를 받는 함수다. Reducer는 필요시 어떻게 state를 업데이트할지 결정하며, 새로운 state를 리턴한다: (state, action) => newState. Reducer는 받은 action type에 따라 event를 처리하는 event listener라고 생각하면 된다.
INFO
Reducer 함수는 Array.reduce() 함수에 전달하는 콜백 함수와 비슷하기 때문에 그 이름을 얻게 되었다.
Reducer는 아래 규칙을 반드시 따라야한다:
- State와 action argument에 기반하여 새로운 state를 계산한다.
- 현재 존재하는 state를 수정할 수는 없다. 대신에, 현재 state를 복사하고, 복사한 값을 수정하여 immutable update 해야 한다.
- 비동기적 로직을 수행하거나, 랜덤 값을 계산하거나, 다른 "side effect"를 발생시킬 수 없다.
왜 Reducer 규칙이 중요하고 어떻게 올바르게 규칙을 따를 수 있는지에 대해서는 차후에 이야기하려 한다.
Reducer 함수 내부 로직은 주로 아래의 단계를 따른다
- Reducer가 해당 action에 관한 처리를 하는지 확인한다.
- 만약 그렇다면 state의 복사본을 만들고, 새로운 값으로 그 복사본을 업데이트한 후 리턴한다. - 그렇지 않다면 현재 state 그대로 리턴한다.
이것은 위의 단계를 보여주는 reducer의 예다:
const initialState = { value: 0 }
function counterReducer(state = initialState, action) {
// Check to see if the reducer cares about this action
if (action.type === 'counter/increment') {
// If so, make a copy of `state`
return {
...state,
// and update the copy with the new value
value: state.value + 1
}
}
// otherwise return the existing state unchanged
return state
}
리듀서는 다음 state가 무엇인지 결정하기위해 어떤 로직이든 사용할 수 있다:
if/else, switch, loops, 등
더 자세한 설명: 왜 Reducer라 불리는가?
Array.reduce() 함수는 어떤 값의 배열에 대해서, 각 값을 한 번에 하나씩 연산하여 만들어진 단일 결과를 리턴한다. 이것을 "배열을 하나의 값으로 줄인다(reduce)"고 생각할 수 있다.
Array.reduce()는 argument로 콜백 함수를 받는데, 이 콜백함수는 배열의 각 값들에 한 번씩 호출된다. Array.reduce()의 콜백함수는 두 개의 argument를 받는다:
- previouseResult, 콜백이 이전에 리턴한 값
- currentItem, 배열의 현재 아이템
콜백이 처음 실행될 때는 previousResult가 없기 때문에, 이때 사용될 초기값을 전달해야한다.
배열에 있는 숫자들을 모두 더한 값을 구하려고 할 때 아래와 같이 reduce 콜백을 작성할 수 있다.
const numbers = [2, 5, 8]
const addNumbers = (previousResult, currentItem) => {
console.log({ previousResult, currentItem })
return previousResult + currentItem
}
const initialValue = 0
const total = numbers.reduce(addNumbers, initialValue)
// {previousResult: 0, currentItem: 2}
// {previousResult: 2, currentItem: 5}
// {previousResult: 7, currentItem: 8}
console.log(total)
// 15
"reduce 콜백" addNumbers가 스스로에 대한 정보를 아무것도 가지고 있지 않음에 유의하자. 콜백은 previousResult와 currentItem argument를 받고 그에 대해 연산한 뒤 새로운 결과값을 리턴한다.
Redux reducer는 "reduce 콜백"과 완전히 같은 개념이다. 리듀서는 "previous result" (state)와 "current item" (action object)를 받고, 이 argument를 바탕으로 새로운 state 값을 계산하여 그를 리턴한다.
Redux action 배열을 만들고, reduce를 호출하고, reducer 함수를 전달하면 동일한 방법으로 결과값을 얻는다:
const actions = [
{ type: 'counter/increment' },
{ type: 'counter/increment' },
{ type: 'counter/increment' }
]
const initialState = { value: 0 }
const finalResult = actions.reduce(counterReducer, initialState)
console.log(finalResult)
// {value: 3}
Redux reducer는 actions 집합을 (시간에 걸쳐서) 하나의 state로 reducing한다고 할 수 있다. 차이점은 Array.reduce()는 reducing이 한 번에 일어나지만, Redux에서는 앱을 실행하는 동안 일어난다.
Store
Redux application state는 store object에 있다.
Store는 reducer를 전달함으로써 만들어지며, 현재 state 값을 리턴하는 getState 함수를 가지고 있다.
import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({ reducer: counterReducer })
console.log(store.getState())
// {value: 0}
Dispatch
Redux store는 dispatch 함수를 가지고 있다. State를 업데이트하는 유일한 방법은 store.dispatch()를 호출하고 action object를 전달하는 것이다. Store는 reducer 함수를 실행하고 새로운 state 값을 저장하며, 우리는 getState()를 호출하여 업데이트된 값을 얻어올 수 있다.
store.dispatch({ type: 'counter/increment' })
console.log(store.getState())
// {value: 1}
Action을 dispatching하는 것을 "이벤트를 촉발시킨다"고 생각하면 된다. 어떤 일이 발생했고, 우리는 store가 그 일의 발생을 알길 원하는 것이다. Reducer는 event listener처럼 동작한다. Reducer가 주시하고 있는 action을 들으면 그 응답으로 state를 업데이트한다.
올바른 action을 dispatching하기 위해 action creator를 호출한다.
const increment = () => {
return {
type: 'counter/increment'
}
}
store.dispatch(increment())
console.log(store.getState())
// {value: 2}
Selectors
Selector는 store state value에서 원하는 정보를 추출하는 함수다. 어플리케이션이 점점 커질수록, 앱의 여러 부분에서 같은 값을 읽기 위해 같은 로직을 반복해서 작성하는 것을 피하게 해준다.
const selectCounterValue = state => state.value
const currentValue = selectCounterValue(store.getState())
console.log(currentValue)
// 2
'PROJECTS' 카테고리의 다른 글
언박싱 몬스터 개선하기(1) - 테스트 환경 만들기 (0) | 2022.01.28 |
---|