공식문서를 공부하기 위해 번역한 내용이기 때문에 의역이나 오역이 존재할 수 있습니다.
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