# Redux Toolkit

可以省去部分步驟，簡單地設定 Redux

## 安裝

```
yarn add react-redux redux @reduxjs/toolkit
```

## 使用步驟

1.設定 provider

```javascript
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`

```javascript
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`

```javascript
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`

```javascript
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

```javascript
import { connect } from 'react-redux'

class App extends React.Component {
  // this.props.dispatch
  // this.props.user
}
export default connect((state) => state)(App);
```

{% embed url="<https://www.softkraft.co/how-to-setup-redux-with-redux-toolkit/>" %}

## Next.js 整合 redux toolkit&#x20;

/pages/\_app.tsx

> 主要增加 \_app.tsx 檔案，其他步驟與上述相同。

```javascript
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 原本作法類似：

```javascript
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&#x20;

如果出現 `(dispatch: any) => Promise` 可以加上如下

1. store.ts 內新增

```javascript
// 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

```javascript
const dispatch = useDispatch<AppDispatch>();
```

<https://redux.js.org/usage/usage-with-typescript>

## Redux toolkit 結合 localstorage

> 用途：存入 state 到 localstorage，使用 redux-localstorage-simple 套件

store.js

```javascript
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>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://easonwang.gitbook.io/web_advance/redux/redux-toolkit.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
