Skip to main content

[Redux] Redux Toolkit (RTK) 筆記

APIs#

configureStore#

configureStore @ Redux Toolkit

在 redux 中原本就有 createStore 這個方法,而 configureStore 可以視為加強版的 createStore,透過 configureStore() 可以簡化設定的流程、結合 slice reducers、添加和 Redux 有關的 middleware、並啟用 Redux DevTools 的擴充套件。

// Before:
import { createStore } from 'redux';
const store = createStore(counter);
// After:
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
reducer: counter,
});

createAction#

createAction @ Redux Toolkit

createAction() 這個工具會根據 action type 回傳對應的 action creator function,這個函式本身內建 toString(),因此可以不再額外定義 TYPE 常數。這個方法有效簡化在 redux 中需要先定義 action type 的常數,然後又在 action 中載入這個 action type 常數的冗長步驟。

透過 createAction 會:

  • 自動產生一個 type 為 「createAction 參數」的 action creator
  • 執行這個 action 時,會把參數的內容直接放入 action.payload
import { createAction } from '@reduxjs/toolkit';
// 透過 createAction 可以產生一個 action creator
const increment = createAction('counter/increment');
let action;
// 自動產生一個 type 為 counter/increment 的 action
action = increment(); // { type: 'counter/increment', payload: undefined }
// 參數內容會自動放入 action.payload 中
action = increment(3); // { type: 'counter/increment', payload: 3 }
// 複寫原本的 toString() 方法,因此可以直接取得該 action 的 type 名稱
increment.toString(); // 'counter/increment'
increment.type; // 'counter/increment'

🔖 備註:action 是一個帶有 typepayload 的「物件」;action creator 是一個會產生 action 的「函式」。因此,這裡的 createAction 實際上是產生一個 action creator 而不是產生一個 action

原本使用 redux 的寫法:

const INCREMENT = 'counter/increment';
// 這是一個 action creator
function increment(amount) {
return {
type: INCREMENT,
payload: amount,
};
}
// 這是一個 action
const action = increment(3); // { type: 'counter/increment', payload: 3 }

createReducer#

createReducer @ Redux Toolkit

createReducer() 這個工具提供你一張 action type 和 reducer 的對應表,而不用使用 switch 語法,此外它自動使用 immer library 這個工具,讓你可以使用「immutable」的方式來變更資料狀態,例如,state.todos[3].completed = true

// 使用 createReducer
const increment = createAction('INCREMENT');
const decrement = createAction('DECREMENT');
// 使用 ES6 中物件的 computed property 語法
// 原本是寫 [increment.type] 但因為 increment 內建 toString() 方法,所以可以寫成 [increment]
const counter = createReducer(0, {
[increment]: (state, action) => state + 1,
[decrement]: (state, action) => state - 1,
});

原本 Redux 中的寫法:

// 這是一個 reducer
const counter = (state = 0, action) => {
if (typeof state === 'undefined') {
return 0;
}
switch (action.type) {
case increment.type:
return state + 1;
case decrement.type:
return state - 1;
default:
return state;
}
};

createSlice#

createSlice @ Redux Toolkit

上面雖然已經有像是 createActioncreateReducer,但如果我們能夠在一個地方同時建立 action creator、reducers 的話,管理起來應該會變得更為方便, createSlice() 於是產生。

createSlice() 這個函式中可以帶入 reducer function, slice name 和 initial state,將自動產生對應的 slice reducer,並包含對應的 action creators 和 action types 在內。

以剛剛的例子來說:

// 原本使用 createAction 和 createReducer
const increment = createAction('INCREMENT');
const decrement = createAction('DECREMENT');
const counter = createReducer(0, {
[increment]: (state) => state + 1,
[decrement]: (state) => state - 1,
});
const store = configureStore({
reducer: counter,
});
document.getElementById('increment').addEventListener('click', () => {
store.dispatch(increment());
});

透過 createSlice 將自動產生 action creatorsaction types

// 使用 createSlice 後
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: (state, action) => state + 1,
decrement: (state, action) => state - 1,
},
});
const store = configureStore({
reducer: counterSlice.reducer,
});
document.getElementById('increment').addEventListener('click', () => {
store.dispatch(counterSlice.actions.increment());
});

在上述 createSlice 產生的 counterSlice 物件中有這些可用的屬性和方法:

// slice 的名稱
counterSlice.name; // "counter"
// actions type
counterSlice.actions.increment.type; // "counter/increment",一樣有內建 toString 方法
counterSlice.actions.decrement.type; // "counter/increment",一樣有內建 toString 方法
// dispatch action
counterSlice.actions.increment(); // counterSlice.actions.increment 是 action creator
counterSlice.actions.decrement();
// reducer
counterSlice.reducer;

createSelector#

createSelector @ Redux Toolkit

createSelector 這個工具來自 Reselect 這個套件。


將 Redux Starter Kit 整合進專案的步驟#

Packages#

STEP 1: Create Reducer#

// ./reducers.js
import { combineReducers } from 'redux';
// import reducers here...
import postsSlice from './slices/posts';
const rootReducer = combineReducers({
// combine reducers here...
posts: postsSlice.reducer,
});
export default rootReducer;

STEP 2: createStore#

// ./store.js
import { configureStore } from 'redux-starter-kit';
import rootReducer from './reducers';
export default function configureAppStore(preloadedState) {
const store = configureStore({
reducer: rootReducer,
preloadedState,
});
// handle hot reloading
if (process.env.NODE_ENV !== 'production' && module.hot) {
module.hot.accept('./reducers', () => store.replaceReducer(rootReducer));
}
return store;
}

STEP 3: Mount Store#

// ./index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import { Provider } from 'react-redux';
import configureAppStore from './store';
const store = configureAppStore();
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
Last updated on