【react+redux】 react使用redux相关内容
首先说一下,文章中所提及的内容都是我自己的个人理解,是我理逻辑的时候,自我说服的方式,如果有问题有补充欢迎在评论区指出。
一、场景描述
为什么在react里面要使用redux,我的理解是因为想要使组件之间的通信更便捷。如下图所示,html中的信息传递只能是通过标签属性来传递,所以如果
我们有一个需求是在某一个组件中获取信息,然后在另一个组件中根据该信息来更改组件显示的内容,这就需要逐层进行信息传递。redux的作用就在于,将该信息保存为全局数据内容,这样所有组件都可以访问到,不用逐层进行信息传递。
二、相关内容介绍
2.1 typescript中的函数调用签名
函数调用签名的作用是什么:定义函数所接收到参数以及返回值的类型。
type addFunctionType = (a: number, b: number) => number;
//这里是函数调用参数,上面定义了所接收参数以及返回值的类型。
const add: addFunctionType = (a, b) => a + b;
export default add;
2.2 问题分析
首先’react-redux’包里的useSelector用于从redux store中获取state的值,在使用该方法时,它的用法如下。
import {useSelector} from ‘react-redux’;
//问题就出在这里,如果用原始的函数这里的state就要对他进行类型声明—用any也可以,减少any的使用可以对代码有一定的收益,例如减少bug
const user = useSelector( state:any => state.user);
然后我们就希望拿到这个state的类型,state就是redux中的store.getstate所获得的数据
解释一下下面所要用到的一小段代码。
type IRootType = typeof store.getState; //取store.getState这个函数的类型
//为什么不是 type IRootType = typeof store.getState();
export type FnReturnType = ReturnType<IRootType>;
//函数调用签名 它的作用是定义一个函数类型,包含函数的参数和返回类型
export const useAppSelector: TypedUseSelectorHook<FnReturnType> = useSelector;
在上述代码中我们所得到的store.getState的类型如下图所示。这个函数是用来获取state参数的,它的返回值应该是个对象,是store中所存储的全局变量–redux中存的数据。
react-redux中useSelector的作用是从redux store中获取state的值。跟直接获取的区别在于:当store中的值发生改变时,会自动渲染组件。
TypedUseSelectorHook的实现如下图所示
interface TypedUseSelectorHook<TState> {
//这里的<Tselected>是传入类型的意思,在后面所输入的第一个函数参数里使用 两个参数一个是selector,接收TState 类型的参数,输出Tselected类型的变量
<TSelected>(
selector: (state: TState) => TSelected,
equalityFn?: EqualityFn<NoInfer<TSelected>>
): TSelected;
<Selected = unknown>(
selector: (state: TState) => Selected,
options?: UseSelectorOptions<Selected>
): Selected;
}
2.3上述内容总结
二、redux相关内容
在介绍redux的内容之前,我们先分析一下需求,一般如果是这样的话,就是在所有组件都能访问到的区域创建数据。
2.1 redux的工作原理
redux的工作原理如下图所示。
图中Store是Redux的一个对象,用于保存应用的状态。–这里就是用于存储全局数据的地方,不能直接更改可以读,需要提供特征的方法进行更改。
Action就是所定义的动作类型,可以包含其他属性,用于传递额外的数据。我的理解就是定义的行为,存储的全局数据可以发生的行为需要提前定义,实际上就是定义的方法。
Reducers:就是根据Action来更新当前的State(全局数据,在react中被定义为状态)。
Dispatch: 用于向Store中发送Action。当调用store.dispatch(action)时,Store会调用reducer来处理action,更新state。
subcribe:注册监听器,当state发生变化的时候,监听器被使用。
2.2原生redux的使用
原生redux在我看来,他就是解决了一个提供所有组件全局变量的一个方案,数据改变之后他还是需要进行手动渲染。react中如果state或者props变化,他就会自动重新渲染组件,所以我们使用的方案是使用一个key state当你变化一次的时候,我们就让key变化一次,导致其重新渲染。
store.ts
import { legacy_createStore as createStore } from "redux";
const reducer = (state, action) => {
if (action.type === "increment") {
state.num += 1;
} else if (action.type === "decrement") {
state.num -= 1;
} else if (action.type === "incrementByAmount") {
state.num += action.payload;
} else if (action.type === "change") {
state.amount = action.payload;
}
return state;
};
const store = createStore(reducer, {
num: 0,
amount: 1,
});
store.subscribe(() => {
console.log("数据发生了变化");
});
export default store;
app.tsx
import React, { useState } from "react";
import store from "./js/store";
function App() {
let [key, setKey] = useState(1);
let dispatch = store.dispatch;
return (
<div className="App">
<h2>测试redux页面</h2>
<br />
<br />
<button
onClick={() => {
dispatch({ type: "increment" });
setKey(key + 1);
}}
>
Increment
</button>
<span>{store.getState().num}</span>
<button
onClick={() => {
dispatch({ type: "decrement" });
setKey(key + 1);
}}
>
Decrement
</button>
<br />
<input
type="text"
value={store.getState().amount}
onChange={(e) => {
dispatch({ type: "change", payload: parseInt(e.target.value) });
setKey(key + 1);
}}
/>
<button
onClick={() => {
dispatch({
type: "incrementByAmount",
payload: store.getState().amount,
});
setKey(key + 1);
}}
>
incrementByAmount
</button>
</div>
);
}
export default App;
三、@reduxjs/toolkit与react-redux的相关内容
原生react在每一个使用stroe的组件文件中都要引用一次store文件,觉得很麻烦,react-redux提供了Provider组件使其只需要引用一次,使得redux更便捷。
index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
//redux的相关内容导入
import { Provider } from "react-redux";
import store from "./app/store";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
在@reduxjs/toolkit工具中,它将initialState、reducer以及actions整合在一起形成切片,更符合传统编程类的概念。
countSlice.ts
import { createSlice } from "@reduxjs/toolkit";
export const counterSlice = createSlice({
// 定义切片名
name: "counter",
// 定义切片中的全局变量
initialState: {
value: 0,
},
// reducer处理器
reducers: {
increment: (state) => {
console.log(state);
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
console.log(action);
state.value += action.payload;
},
},
});
// 生成action
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
TestRedux.tsx
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
decrement,
increment,
incrementByAmount,
} from "../features/counter/counterSlice";
export default function TestRedux() {
// 获取变量
const count = useSelector((state: any) => state.counter.value);
// 处理action
const dispatch = useDispatch();
const [amount, setAmount] = useState(1);
return (
<div>
<h2>测试redux页面</h2>
<br />
<br />
<button onClick={() => dispatch(increment())}>Increment</button>
<span>{count}</span>
<button onClick={() => dispatch(decrement())}>Decrement</button>
<br />
<input
type="text"
value={amount}
onChange={(e) => {
setAmount(parseInt(e.target.value));
}}
/>
<button onClick={() => dispatch(incrementByAmount(amount))}>
Decrement
</button>
</div>
);
}