可以省去部分步驟,簡單地設定 Redux
安裝
Copy yarn add react-redux redux @reduxjs/toolkit
使用步驟
1.設定 provider
Copy import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root'),
);
2.新增 store
/store/index.js
Copy import { configureStore } from '@reduxjs/toolkit';
import { combineReducers } from 'redux';
import user from './user';
const reducer = combineReducers({
user
})
const store = configureStore({
reducer,
})
export default store;
3.新增 redux-toolkit 版本的簡化 reducer
/store/user.js
這邊記得 export default slice.reducer
不是 slice.reducers
Copy import { createSlice } from '@reduxjs/toolkit'
const slice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: (state, action) => state + action.payload,
decrement: (state, action) => state - action.payload
}
})
export default slice.reducer
const { increment, decrement } = slice.actions
export const doIncrement = (num) => async dispatch => {
// do some async fetch here
return dispatch(increment(num))
}
4.從 component 發送 action
App.js
Copy import { useDispatch, useSelector } from 'react-redux'
import { doIncrement } from './store/user';
const App = () => {
const dispatch = useDispatch()
const user = useSelector(state => state.user)
const handleSave = () => {
dispatch(doIncrement(2))
}
.....省略
如果用 Class component 可以使用 connect 接入 store state 與 dispatch
Copy import { connect } from 'react-redux'
class App extends React.Component {
// this.props.dispatch
// this.props.user
}
export default connect((state) => state)(App);
Next.js 整合 redux toolkit
/pages/_app.tsx
主要增加 _app.tsx 檔案,其他步驟與上述相同。
Copy import { Provider } from 'react-redux';
import type { AppProps } from 'next/app';
import store from '../store/index';
function App({
Component, pageProps,
}: AppProps) {
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
export default App;
動態 State 內容
與 Redux 原本作法類似:
Copy import { createSlice } from '@reduxjs/toolkit'
const slice = createSlice({
name: 'totalBalance',
initialState: {},
reducers: {
updateUserTotalBalance: (state, action) => ({
...state,
[action.payload.protocolName]: state[action.payload.protocolName]
? state[action.payload.protocolName] + action.payload.num
: action.payload.num
}),
}
})
export default slice.reducer
const { updateUserTotalBalance } = slice.actions
export const doUpdateUserTotalBalance = (num, protocolName) => async dispatch => {
// do some async fetch here
return dispatch(updateUserTotalBalance({ num, protocolName }))
}
加入 Typescript
如果出現 (dispatch: any) => Promise
可以加上如下
Copy // Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
2.component 內 useDispatch 加上 type
Copy const dispatch = useDispatch<AppDispatch>();
https://redux.js.org/usage/usage-with-typescript
Redux toolkit 結合 localstorage
用途:存入 state 到 localstorage,使用 redux-localstorage-simple 套件
store.js
Copy import { configureStore } from '@reduxjs/toolkit';
import { combineReducers} from 'redux';
import { save, load } from "redux-localstorage-simple"
import user from './user.js';
const reducer = combineReducers({
user
})
const store = configureStore({
reducer,
middleware: (getDefaultMiddleware) => [
...getDefaultMiddleware(),
save(),
],
preloadedState: load(),
})
export default store;
如果用 next.js 出現 Hydration failed because the initial UI does not match what was rendered on the server
可以參考:https://github.com/vercel/next.js/discussions/35773#discussioncomment-2840696
persist 也可參考官方使用的另一個套件 redux-persist: https://redux-toolkit.js.org/usage/usage-guide#use-with-redux-persist