Redux는 앱의 상태를 actions이라는 이벤트를 통해 관리하고 업데이트하는 패턴이자 라이브러리다.
리덕스로 만들어진 패턴과 도구들은 변화가 발생했을 때 언제, 어디서, 왜, 어떻게 앱의 상태가 업데이트 되는지 로직이 어떻게 행동할지 이해하기 더 쉽게 만든다.
상태는 특정 시점의 앱 상태를 설명한다. ui는 해당 상태를 기준으로 렌더링 된다. 사용자가 버튼을 클릭하는 것과 같은 일이 발생하면 발생한 일에 따라 상태가 업데이트된다. ui가 새 상태를 기준으로 다시 렌더링된다.
단순성이 깨질 수 있다. 그렇기에 컴포넌트에서 공유 상태를 추출하고, 컴포넌트 트리 외부의 중앙 집중식 위치에 넣어 해결한다. 이를 통해 우리의 컴포넌트 트리는 큰 "뷰"가 되고, 어떤 컴포넌트든 트리의 위치에 상관없이 상태에 접근하거나 동작을 트리거 할 수 있다. 뷰와 상태의 독립성을 유지하므로써 코드에 더 많은 구조와 유지 가능성을 부여한다.
immutable은 바꿀 수 없음을 의미한다. 불변성을 유지하기 위해 기존 개체/배열의 복사본을 만든 다음 복사본을 수정한다.
action은 type field를 가진 일반적인 자바스크립트 객체이다. 액션은 앱에서 일어난 일을 설명하는 이벤트라고 생각할 수 있다. type field는 "todos/todoAdded"와 같이 이작업을 설명하는 이름을 부여하는 문자열이어야 한다. 일반적으로 "domain/eventName"과 같은 유형의 문자열을 작성한다. 첫 번째 부분은 이 작업이 속한 기능 또는 범주이고 두 번째 부분은 발생한 특정 작업이다.
액션 생성자는 액션 오브젝트를 생성하고 반환하는 함수이다.
reducer는 현재 상태와 액션 오브젝트를 받아 필요에 따라 상태를 업데이트할 방법을 결정하고 새로운 상태를 반환하는 함수이다. reducer는 수신한 액션(이벤트) 유형에 따라 이벤트를 처리하는 이벤트 리스너라고 생각할 수 있다.
reducer는 몇가지 조건을 지켜야 한다.
현재 Redux앱의 상태는 store라는 객체에 존재. store는 reducer를 통과하여 생성되며, 현재 상태 값을 반환하는 getState라는 메서드가 있다.
Redux store는 dispatch라는 함수를 가진다. state를 업데이트하는 유일한 방법은 store.dispatch()를 호출하고 액션 객체를 전달하는 것이다.
selectors는 store 상태 값에서 특정 정보를 추출하는 함수이다.
Redux Store는 configureStore를 이용해 만들어진다. configureStore에는 reducer인수를 전달해야 한다
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export default configureStore({
reducer: {
counter: counterReducer
}
})
우리의 앱은 다양한 기능으로 구성될 수 있다.
그리고 각각의 기능은 자신의 reducer function을 가지고 있다. 우리가 configureStore를 호출할 때 객체의 다양한 리듀서를 모두 전달할 수 있다. 객체의 키 이름은 최종 상태 값의 키를 결정한다.
slice는 Redux reducer logic과 액션들의 모음이다.
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
increment: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
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
export const selectCount = state => state.counter.value
const count = useSelector(selectCount)
컴포넌트는 Redux store와 직접 통신할 수 없다. 왜냐하면 Redux Store를 컴포넌트 파일에 import 할 수 없기 때문에. 그 대신 useSelector는 우리를 위해 뒤에서 Redux store와 통신을 처리한다. selector함수를 전달하면 someSelector(store.getState())가 호출되고 결과가 반환된다.
작업이 전달되고 Redux store가 업데이트 될 때마다 useSelector는 선택기 기능을 다시 실행한다. 선택기가 지난번과 다른 값을 반환하면 useSelector는 컴포넌트가 새 값으로 다시 렌더링 되는지 확인한다.