redux & react-redux & @reduxjs/toolkit

redux团队先后推出了redux、react-redux、@reduxjs/toolkit,这三个库的api各有不同。本篇文章就来梳理一下当我们需要在项目中集成redux,从直接使用redux,到使用react-redux,再到react-redux和@reduxjs/toolkit配合使用,分别需要调用到哪些api。

一、在react中集成redux

redux中通过createStore可以创建出store实例,在该实例上存在三个实例方法。

通过store.subscribe可以监听到state的改变,引起组件的重新渲染。

通过dispatch可以分发action,引起redux内部state的更新。

通过getState可以获取到redux内部的state。

以一个简单的计数器程序作为示例。

首先在App组件中引入Count组件,为其传入store实例作为props。

import React from "react";
import Count from "./components/Count";
import store from "./store";

export default function App() {
  return <Count store={store} />;
}

在Count组件中,我们在组件挂载后通过store.subscribe监听redux的state,通过store,getState获取state,为按钮绑定点击事件,回调调用dispatch,传入action的返回值作为参数。

import React, { useEffect, useState } from "react";
import store from "../store";
import { createAddActions } from "../store/actions/count";

export default function Count() {
  const [_, rerender] = useState({});
  useEffect(() => {
    store.subscribe(() => rerender({}));
  }, []);
  return (
    <div>
      <div>sum: {store.getState()}</div>
      <button onClick={() => store.dispatch(createAddActions(1))}>
        点我+1
      </button>
    </div>
  );
}

接下来是store的编写。

// store/index.js

import { countReducer } from "./reducers/count";
import { createStore } from "redux";

export default createStore(countReducer);

reducers必须是一个纯函数,纯函数的概念是需要返回一个新的state替换原来的state,而不是直接在原来的state上修改。

// store/reducers/count.js

export function countReducer(prevState, action) {
  switch (action.type) {
    case "add":
      return prevState + action.value;
    default:
      return 0;
  };
};

action的实现。

// store/actions/count.js

export function createAddActions(value) {
  return {
    type: "add",
    value,
  };
};

二、在react中集成react-redux

react-redux提出了容器组件的概念,要求redux只能和容器组件进行通信,至于ui组件,只能通过props来获取容器组件传入的state和引起redux内部state改变的callback。

引入容器组件的目的在于尽量让ui组件内部看起来与其他不需要使用redux的组件无异。

如何理解无异呢?就是不要直接去调用store的实例方法,而是调用父组件提供的方法。当我们调用父组件提供的方法,而react-redux会再调用store的实例方法。

store.getState() -> props.state

store.dispatch() -> props.callback()

store.subscribe不再手动调用,而是由react-redux执行。

Count.js修改如下。

import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { createAddActions } from "../store/actions/count";

function Count(props) {
  return (
    <div>
      <div>sum: {props.state}</div>
      <button onClick={() => props.add(1)}>点我+1</button>
    </div>
  );
}

export default connect(
  (state) => ({ state }),
  (dispatch) => ({
    add: (number) => dispatch(createAddActions(number)),
  })
)(Count);

connect的第二个参数还可以简写为对象形式。

export default connect((state) => ({ state }), { add: createAddActions })(Count);

我们使用redux的目的是在多个组件之间共享state,所以我们直接引用redux时,需要在多个组件中传入store作为props,react-redux针对此也做了优化。

我们只需引入Provider包裹App组件,在Provider中传入store作为props,我们就可以在容器组件中获取到props。其实底层就是调用了react的SomeContext.Provider,内部组件只要使用useContext就可以获取到store。

// index.js

import React from "react";
import ReactDOM from 'react-dom/client';
import { Provider } from "react-redux";
import store from "./store";
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
        <App />
    </Provider>
);

在Count组件中,我们不再需要手动传入store。

import React from "react";
import Count from "./components/Count";

export default function App() {
  return <Count />;
}

在现在的react-redux中,更推荐使用useSelector和useDispatch来代替connect。当我们使用useSelector和useDispatch获取state和分发action时,不再存在container组件,取而代之的是selector hook和dispatch hook。

对于connect而言,会优先从props中获取store,不存在的情况下再用React.useContext获取props,所以store有两种传入方式。

对于useSelector和useDispatch而言,他们只是Hook而不是组件,没有props,只能从React.useContext中获取store,所以如果外层没有Provider组件的话,项目是无法运行的。

此外,connect和Provider都实现了对store的监听。

import React, { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { createAddActions } from "../store/actions/count";

export default function Count() {
  const state = useSelector((state) => state);
  const dispatch = useDispatch();
  return (
    <div>
      <div>sum: {state}</div>
      <button onClick={() => dispatch(createAddActions(1))}>点我+1</button>
    </div>
  );
}

三、在React中集成@reduxjs/toolkit和react-redux

@reduxjs/toolkit的作用如下:

1. 将initialState、reducers、actions整合在一起形成切片

2. 自动生成action,{name: sliceName/reducerName, payload: value}

3. 代替redux/thunk实现异步action功能

// store/slices/count.js

import { createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    add: (state, action) => {
      return state + action.payload;
    },
  },
});

export const { add } = counterSlice.actions;

export default counterSlice.reducer;
// store/index.js

import countReducer from "./slices/count";
import { configureStore } from "@reduxjs/toolkit";

export default configureStore({
  reducer: countReducer // reducer的结构对应state的结构
});

### 集成 Redux Toolkitredux-persist 在 TypeScript 中 为了实现 Redux Toolkit `redux-persist` 的集成,在项目中需安装必要的依赖包。通过命令行工具可以轻松完成这些操作: ```bash npm install @reduxjs/toolkit react-redux redux-persist persist-rehydrate ``` 创建 store 文件时,应配置持久化存储逻辑并定义根 reducer。下面是一个完整的例子来展示如何设置这一过程[^1]。 #### 创建 Store 并应用 Middleware 在文件顶部引入所需的模块之后,使用 `configureStore()` 方法来自动生成带有中间件支持的标准 Redux 商店实例。这里特别要注意的是加入了 `createWrapper` 来处理服务器端渲染的情况以及 `persistReducer` 函数用于指定哪些部分的数据需要被保存到本地缓存中。 ```typescript // src/store.ts import { configureStore } from &#39;@reduxjs/toolkit&#39;; import { persistStore, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, } from &#39;redux-persist&#39;; const rootReducer = (state: any, action: AnyAction) =&gt; { switch (action.type) { case CLEAR_STORAGE: state = undefined; break; default: break; } return appReducer(state, action); }; export const makeStore = () =&gt; configureStore({ reducer: persistedReducer, middleware: getDefaultMiddleware =&gt; getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }), }); let store = makeStore(); export const wrappper = createWrapper(makeStore); export type RootState = ReturnType&lt;typeof store.getState&gt;; export type AppDispatch = typeof store.dispatch; export default store; ``` #### 定义 Root Reducer 及 Persist Config 接着要做的就是编写 root reducer 负责管理整个应用程序的状态树结构;同时也要设定好 `persistConfig`, 这样才能告诉 `redux-persist` 哪些数据应该被永久保留下来。对于敏感信息不建议进行持久化处理以免造成安全隐患。 ```typescript // src/reducers/rootReducer.ts import storage from &#39;redux-persist/lib/storage&#39;; // defaults to localStorage for web import { combineReducers } from &#39;@reduxjs/toolkit&#39;; import { persistReducer } from &#39;redux-persist&#39;; import userSlice from &#39;./userSlice&#39;; const persistConfig = { key: &#39;root&#39;, version: 1, storage, blacklist: [&#39;sensitiveData&#39;], // 不会持久化的reducer名称数组 }; const reducers = combineReducers({ user: userSlice.reducer, }); const persistedReducer = persistReducer(persistConfig, reducers); export default persistedReducer; ``` 最后一步是在组件内部连接 React 应用程序新建立好的 Redux 存储库之间关系。这通常涉及到提供者模式的应用,即包裹顶层组件以使所有子级都能访问全局状态。 ```jsx // src/index.jsx 或 index.tsx import React from &#39;react&#39;; import ReactDOM from &#39;react-dom/client&#39;; import { Provider } from &#39;react-redux&#39;; import { PersistGate } from &#39;redux-persist/integration/react&#39;; import { store, persistor } from &#39;./store&#39;; import App from &#39;./App&#39;; const container = document.getElementById(&#39;app&#39;); if (!container) throw new Error(&#39;Failed to find the root element&#39;); const root = ReactDOM.createRoot(container); root.render( &lt;Provider store={store}&gt; &lt;PersistGate loading={&lt;div&gt;Loading...&lt;/div&gt;} persistor={persistor}&gt; &lt;App /&gt; &lt;/PersistGate&gt; &lt;/Provider&gt;, ); ``` 这样就完成了 Redux Toolkit 结合 `redux-persist` 实现简单而强大的前端状态管理持久化方案的搭建工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值