공식문서를 공부하기 위해 번역한 내용이기 때문에 의역이나 오역이 존재할 수 있습니다.
function Counter() {
// State: a counter value
const [counter, setCounter] = useState(0)
// Action: code that causes an update to the state when something happens
const increment = () => {
setCounter(prevCounter => prevCounter + 1)
}
// View: the UI definition
return (
<div>
Value: {counter} <button onClick={increment}>Increment</button>
</div>
)
}
다수의 컴포넌트에서 같은 상태 값을 공유하거나 사용해야한다면 단방향 데이터 흐름을 지키지 못할 수 있다. 특히나 컴포넌트들이 다른 폴더들에 위치했을 때 더 일어나기 쉽다. 부모 컴포넌트로 상태를 끌어올려 이 문제를 해결할 수 있겠지만 항상 가능한 것은 아니다.
위 문제를 해결할 수 있는 방법은 컴포넌트들 속 공유된 상태 값을 뽑아내 컴포넌트 트리 구조 바깥에 마련된 장소에 모으는 것이다. 위 과정을 통해 컴포넌트 트리는 하나의 큰 뷰 단위가 되고 어떠한 컴포넌트들도 상태 값이나 트리거가 될 수 있는 액션에 접근할 수 있게된다.
위와 같은 일련의 과정을 통해 뷰와 상태를 분리시켜 독립적으로 유지할 수 있게된다. 이는 코드가 더욱 구조적이며 유지보수하기 쉽게 만들어 준다.
쉽게 말해 하나의 장소에서 상태를 관리하고, 상태를 액션에 따라 예측가능하게 업데이트하는 것이 리덕스의 기초 아이디어다.
자바스크립트의 객체나 배열들은 항상 가변성을 가지고 있다.
const obj = { a: 1, b: 2 }
// still the same object outside, but the contents have changed
obj.b = 3
const arr = ['a', 'b']
// In the same way, we can change the contents of this array
arr.push('c')
arr[1] = 'd'
위와 같이 간단하게 데이터의 내용을 수정할 수 있다.
불변성을 유지하기 위해선, 코드를 반드시 복사해 그 복사본을 가공해야한다.
리덕스는 모든 상태 값 업데이트가 불변성을 유지해야한다고 생각한다.
type
을 키 값으로 가진 순수한 자바스크립트 객체domain/eventName
으로 정의type
외에도 payload
라는 키 값이 존재한다const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
action
객체를 생성하거나 반환하는 함수action
객체를 수시로 작성할 필요가 없다const addTodo = text => {
return {
type: 'todos/todoAdded',
payload: text
}
}
state
와 action
을 인자로 받는 함수state
를 업데이트 할지 결정state
를 반환action
기준으로 작동하는 event listener로 이해하면 편하다state
는 인자로 받는 state
와 action
을 기반으로 작동된다state
를 변경하는 것을 허용하지 않는다. 대신 state
를 복사해 바꾸는 형식으로 변화를 일으킨다 (불변성 유지)action
을 감지할 것인지 체크한다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
값이 모이는 곳reducer
를 통해 store
가 생성되며, getState
라는 메서드를 통해 현재 상태 값을 반환한다import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({ reducer: counterReducer })
console.log(store.getState())
// {value: 0}
store
은 dispatch
라는 메서드를 가지고 있다.store.dispatch
를 통해 action 객체를 전달하는 것store
가 reducer
함수를 작동시켜 새로운 state
를 만들어 getState
를 통해 state
를 업데이트시킨다store.dispatch({ type: 'counter/increment' })
console.log(store.getState())
// {value: 1}
action
을 dispatch
하는 것이란 이벤트를 트리거화 하는 것이라고 생각하면 좋다.store
에게 알린 뒤reducer
는 event listener
처럼 작동하고 action
을 캐치한 뒤state
값을 업데이트 한다action
을 dispatch
하기 위해 action creator
를 사용한다.const increment = () => {
return {
type: 'counter/increment'
}
}
store.dispatch(increment())
console.log(store.getState())
// {value: 2}
const selectCounterValue = state => state.value
const currentValue = selectCounterValue(store.getState())
console.log(currentValue)
// 2
store
는 root reducer
함수에 의해 생성된다store
는 root reducer
에 의해 한 번 호출되고, 그 값을 초기 state
로 저장해 값을 반환한다store
에 저장된 현재 state
에 접근해 렌더링 될 때 사용된다state
값이 바뀔 때 감지할 수 있는 상태가 된다state
값의 변화가 발생한다dispatch({type: 'counter/increment'})
처럼 store
의 action
에게 dispatch
가 된다store
는 previous state
와 current action
을 인자로 받는 reducer
함수를 실행시켜 new state
를 반환한다store
은 관련된 UI들에게 값이 업데이트 됐다고 알린다state
값이 바뀌었는지 체크한다state
값으로 재렌더링되어 새로운 값이 스크린에 노출되게 된다.https://ko.redux.js.org/tutorials/essentials/part-1-overview-concepts