2024.02.07(수)
A library for managing global application state 🔗
npm install @reduxjs/toolkit react-redux
src/store.tsx
생성import { configureStore } from "@reduxjs/toolkit";
export default configureStore({
reducer: {
}
});
src/index.tsx
에 store와 Provider
import 후 다음과 같이 감싸고 store 넘기기 (생성한 store를 사용할 수 있도록 해줌)import store from './store';
import { Provider } from 'react-redux';
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
description | |
---|---|
State | State는 특정 시점의 app의 상태를 설명한다. |
State → View | UI는 해당 state를 기반으로 render된다. |
Actions → State | event가 발생하면 그에 따라 state가 update된다. |
State → View | UI는 새로운 state를 기반으로 re-render된다. |
type
field가 있는 일반 JavaScript 객체
👉application에서 발생한 일을 설명하는 event
const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
type
field"domain/eventName"
과 같은 유형의 문자열 작성domain
: 해당 action이 속한 feature 또는 categoryeventName
: 발생한 특정한 일payload
fieldAction 객체를 생성하고 반환하는 함수
const addTodo = text => {
return {
type: 'todos/todoAdded',
payload: text
}
}
현재
state
와action
객체를 받아 필요한 경우 state를 update하는 방법을 결정하고 새로운 state를 반환하는 함수:(state, action) => newState
👉받은 action (event) type을 기반으로 event를 처리하는 event listener
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
}
Array.reduce()
method에 넘겨주는 callback function과 비슷한 개념이라 여기서 이름을 따왔다고 함===
를 사용하여 shallow equality check를 함현재 Redux application이 살아있는(저장되어 있는) 객체
import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({ reducer: counterReducer })
console.log(store.getState()) // return the current state value
// {value: 0}
getState
라는 method가 있음Redux store가 가진 method로 state를 update하기 위한 유일한 방법
👉actions를 dispatching(파견)하는 event trigger
store.dispatch({ type: 'counter/increment' })
console.log(store.getState())
// {value: 1}
const increment = () => {
return {
type: 'counter/increment'
}
}
store.dispatch(increment())
console.log(store.getState())
// {value: 2}
store.dispatch()
에 action 객체를 전달해서 호출하면, store는 reducer 함수를 실행하고 내부에 새 state value를 저장getState()
를 호출해 update된 value를 찾아볼 수 있음store state value에서 특정 정보를 추출하는 방법을 아는 함수
const selectCounterValue = state => state.value
const currentValue = selectCounterValue(store.getState())
console.log(currentValue)
// 2
dispatch({type: 'counter/increment'})
와 같이 Redux store에 action을 dispatch함
application의 global state는 모두 단일 store 내의 object tree에 저장되어 관리되어야 한다.
state는 immutable(read-only) data이며, action 객체만이 state를 변경할 수 있다.
state tree가 actions에 의해 어떻게 변화되었는지 명시하려면 순수 함수인 reducer를 작성해라.
configureStore
APIold redux
import { createStore, applyMiddleware, combineReducers, compose } from 'redux'
import thunk from 'redux-thunk'
import postsReducer from '../reducers/postsReducer'
import usersReducer from '../reducers/usersReducer'
const rootReducer = combineReducers({
posts: postsReducer,
users: usersReducer
})
// middleware: action이 dispatch된 후 reducer에서 해당 action을 받아와서 업데이트하기 전에 추가적인 작업을 할 수 있음
const middlewareEnhancer = applyMiddleware(thunk)
const composeWithDevTools =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const composedEnhancers = composeWithDevTools(middlewareEnhancer)
const store = createStore(rootReducer, composedEnhancers)
modern redux
import { configureStore } from '@reduxjs/toolkit'
import postsReducer from '../reducers/postsReducer'
import usersReducer from '../reducers/usersReducer'
// Automatically adds the thunk middleware and the Redux DevTools extension
const store = configureStore({
// Automatically calls `combineReducers`
reducer: {
posts: postsReducer,
users: usersReducer
}
})
configureStore
= createStore
wrappercreateSlice
APIold redux
export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
old redux
import { ADD_TODO, TOGGLE_TODO } from '../constants/todos'
export const addTodo = (id, text) => ({
type: ADD_TODO,
text,
id
})
export const toggleTodo = id => ({
type: TOGGLE_TODO,
id
})
old redux
import { ADD_TODO, TOGGLE_TODO } from '../constants/todos'
const initialState = []
export default function todosReducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO: {
return state.concat({
id: action.id,
text: action.text,
completed: false
})
}
case TOGGLE_TODO: {
return state.map(todo => {
if (todo.id !== action.id) {
return todo
}
return {
...todo,
completed: !todo.completed
}
})
}
default:
return state
}
}
modern redux
import { createSlice } from '@reduxjs/toolkit'
const initialState = []
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
todoAdded(state, action) {
const { id, text } = action.payload
// "Mutating" update syntax thanks to Immer, and no `return` needed
// 자동으로 exsiting state를 반환해줌
state.todos.push({
id,
text,
completed: false
})
},
todoToggled(state, action) {
const matchingTodo = state.todos.find(todo => todo.id === action.payload)
if (matchingTodo) {
// Can directly "mutate" the nested object
matchingTodo.completed = !matchingTodo.completed
}
}
}
})
// `createSlice` automatically generated action creators with these names.
// export them as named exports from this "slice" file
export const { todoAdded, todoToggled } = todosSlice.actions
// Export the slice reducer as the default export
export default todosSlice.reducer
without createSlice | with createSlice |
---|---|
action creators를 직접 정의 | 우리가 작성한 reducer 함수를 기반으로 action creators를 자동으로 만들어줌 |
action type을 기반으로 switch/case 문으로 reducer logic 직접 작성 | 한 객체 안에 case에 따른 reducer 함수들을 정의 가능 |
immutable update logic 사용 | Immer라는 불변성 관리 라이브러리를 사용해서 불변성을 신경쓰지 않으면서 보기 쉽고 더 짧은 코드로 state update 가능 (복사본을 만들어서 immutable update를 하는 방식도 사용 가능) |
파일 분리 ⭕ | 파일 분리 ❌ |
type of code로 폴더 구성 | features 별로 폴더 구성 |
createSlice(name, initialState, reducers)
name
: 생성되는 action type의 prefix로 사용될 문자열 (domain)initialState
: reducer의 초기 상태reducers
: 문자열 key와 특정 action을 처리하는 “case reducer” 함수를 value로 하는 객체action.payload
field에 저장todoToggled(42) // {type: 'todos/todoToggled', payload: 42}
todoAdded({id, text}) // {type: 'todos/todoAdded', payload: 42}