我是靠谱客的博主 傻傻电源,这篇文章主要介绍redux-toolkit 在 react 中的基础用法,现在分享给大家,希望可以做个参考。

更多分享内容可访问我的个人博客

https://www.niuiic.top/

本文通过几个例子来帮助初学者快速入门 redux-toolkit 在 react 的应用,不涉及异步 action 以及与服务器的交互,不解释 redux 的原理和用法,读者应当对 react 和 redux 有一定了解。

quickstart

先来看一个简单的案例,这是redux toolkit document中的例程。

首先来看最终在 react 中使用 redux 的部分。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// features/counter/Counter.tsx import React from 'react' import { RootState } from '../../app/store' import { useSelector, useDispatch } from 'react-redux' import { decrement, increment } from './counterSlice' export function Counter() { const count = useSelector((state: RootState) => state.counter.value) const dispatch = useDispatch() return ( <div> <div> <button aria-label="Increment value" onClick={() => dispatch(increment())} > Increment </button> <span>{count}</span> <button aria-label="Decrement value" onClick={() => dispatch(decrement())} > Decrement </button> </div> </div> ) }

这一段代码实现了一个计数器组件。该组件有两个按钮,一个做加法一个做减法。另外还有一组用来显示数据的<span>

光看 return 部分,最重要的无非就是count以及dispatch。这两个都在前面有定义。其中count=…value,是一个获取计数器的数据的函数,dispatchuseDispatch的结果。dispatch的参数是一个函数的返回值,那么这个函数显然是 action creator。

注意用 useSelector 获取数据只有在触发 action -> reducer 这一通操作时才会更新。如果只用这个函数获取数据的话,所有修改数据的操作都必须经过这一流程。

现在的问题是 action creator 从哪里来。看引入语句,来到features/counter/counterSlice.ts

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// features/counter/counterSlice.ts import { createSlice, PayloadAction } from "@reduxjs/toolkit"; export interface CounterState { value: number; } const initialState: CounterState = { value: 0, }; export const counterSlice = createSlice({ name: "counter", initialState, reducers: { increment: (state) => { // Redux Toolkit allows us to write "mutating" logic in reducers. It // doesn't actually mutate the state because it uses the Immer library, // which detects changes to a "draft state" and produces a brand new // immutable state based off those changes state.value += 1; }, decrement: (state) => { state.value -= 1; }, incrementByAmount: (state, action: PayloadAction<number>) => { state.value += action.payload; }, }, }); // Action creators are generated for each case reducer function export const { increment, decrement, incrementByAmount } = counterSlice.actions; export default counterSlice.reducer;

看到最后的导出语句,这些 action creators 来自counterSlice.actions。那么这个值是从哪里来的呢。其实是自动生成的。看到createSlice这部分,传入的参数包括 Slice 的名字,state 的初始值,reducers。这些 action creators 就是根据传入的 reducers 自动生成的。同时,看这些 reducers,可以发现它们不再是纯函数,而可以在内部修改传入参数的值。

那么 action creators 的来历搞清楚了,再看到最后导出的counterSlice.reducer,看看它去哪了。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// app/store.ts import { configureStore } from "@reduxjs/toolkit"; import counterReducer from "../features/counter/counterSlice"; export const store = configureStore({ reducer: { counter: counterReducer, }, }); // 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;

这是生成 store 的代码,刚才导出的 reducers 在这里被configureStore接收,字段名是刚才给counterSlice起的名字counter

案例没有给出的最后一步就是Provider。这里有个例子可以看一下。welcome 是一个页面,login 是该页面中的一个组件。这里的 welcomeStore 就是上文的 store(一般来说整个程序只需要一个 store,不同的部分可以用不同的 slice 划分)。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
import { Provider } from "react-redux"; import { Login } from "../components/welcome/Login"; import { welcomeStore } from "../stores/welcome"; export default function Welcome() { return ( <Provider store={welcomeStore}> <Login /> </Provider> ); }

至此,所有的流程走完了,比起不用工具,省略了最恶心的定义 actionsTypes、actions、action creators 的过程(由于使用 typescript,需要对该部分进行类型限制,会产生大量代码)和同样麻烦的配置容器组件的过程。

注意useSelectoruseDispatch只能用在函数式组件中,如果非要用到 class 组件,那么只能像下面这样写。但显然有点麻烦,非必要就算了。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class App extends Component { constructor(props){ super(props) this.state = { reduxState : {} } } DummyView = () => { const reducer = useSelector(state => state.reducer) useEffect(() => { this.setState({ reduxState : reducer }) }, []) return null } render(){ return( <this.DummyView/> ) } }

reducer 传参

查看刚才写的 reducer,发现已经有一个参数state。但这显然是不够的,比如有一个输入框,现在希望将输入的内容显示在框内。首先显示的部分很容易,只要把 state 中的东西写到 InputText 组件的 value 中即可。但是如何将输入的内容存到 store 中呢。这需要 reducer 有第二个参数。来看以下案例。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export const loginSlice = createSlice({ name: "login", initialState, reducers: { login: (state) => { if (state.username == "user" && state.password == "password") { state.authentication = true; } else { state.authentication = false; } }, setUsername: (state, action) => { state.username = action.payload; }, setPassword: (state, action) => { state.password = action.payload; }, }, }); <TextInput value={loginState.username} onChangeText={(value) => dispatch(setUsername(value))} />

首先,reducer 的第二个参数就叫 action,action 的字段就取 payload,不要改就行了。用法也很简单,传进去什么就是什么。具体可见最后的组件部分代码。

类型安全

quickstart 中的案例没有进行严格的类型限制,下面再通过一个案例来看使用 typescript 时的正常写法。

该案例是一个 react native 项目,功能是实现一个简单的登陆功能,项目结构如下。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
stores/ page1.ts pages/ page1.tsx page1.scss hooks/ page1.ts components/ page1/ component1.tsx component1Redux.ts component1.scss

这不是完整的目录结构,没用到的暂时没加

stores/Welcome.ts没有改变。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
import { configureStore } from "@reduxjs/toolkit"; import loginReducer from "../components/welcome/LoginRedux"; export const welcomeStore = configureStore({ reducer: { login: loginReducer, }, }); export type WelcomeState = ReturnType<typeof welcomeStore.getState>; export type WelcomeDispatch = typeof welcomeStore.dispatch;

hooks/Welcome.ts是新增的内容,这里给useDispatchuseWelcomeSelector添加了类型限定。

复制代码
1
2
3
4
5
6
7
8
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import type { WelcomeState, WelcomeDispatch } from "../stores/welcome"; export const useWelcomeDispatch = () => useDispatch<WelcomeDispatch>(); export const useWelcomeSelector: TypedUseSelectorHook<WelcomeState> = useSelector;

components/welcome/LoginRedux.ts中对initialState的类型定义方式做了改变,且增加了对 reducer 第二个参数的类型限定。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { createSlice, PayloadAction } from "@reduxjs/toolkit"; interface LoginState { username: string; password: string; authentication: boolean; } const initialState = { username: "", password: "", authentication: false, } as LoginState; // 根据文档,改成这样是为了避免typescript无必要地收紧initialState的类型 export const loginSlice = createSlice({ name: "login", initialState, reducers: { login: (state) => { if (state.username == "user" && state.password == "password") { state.authentication = true; } else { state.authentication = false; } }, // 这里对payload的类型进行了限定 setUsername: (state, action: PayloadAction<string>) => { state.username = action.payload; }, setPassword: (state, action: PayloadAction<string>) => { state.password = action.payload; }, }, }); export const { login, setUsername, setPassword } = loginSlice.actions; export default loginSlice.reducer;

components/login.tsx如下所示。注意此时用的是 hooks 导出的两个函数。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { TextInput, Button, View, Text } from "react-native"; import { useWelcomeDispatch, useWelcomeSelector } from "../../hooks/Welcome"; import { login, setUsername, setPassword } from "./LoginRedux"; export function Login() { const loginState = useWelcomeSelector((state) => state.login); const dispatch = useWelcomeDispatch(); return ( <View> <Text>Username: </Text> <TextInput value={loginState.username} onChangeText={(value) => dispatch(setUsername(value))} /> <Text>Password: </Text> <TextInput value={loginState.password} onChangeText={(value) => dispatch(setPassword(value))} /> <Button title="login" onPress={() => dispatch(login())} /> <Text>{loginState.authentication ? "success" : "fail"}</Text> </View> ); }

pages/welcome.tsx如下,把Provider加上即可。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
import { Provider } from "react-redux"; import { Login } from "../components/welcome/Login"; import { welcomeStore } from "../stores/Welcome"; export default function Welcome() { return ( <Provider store={welcomeStore}> <Login /> </Provider> ); }

最后

以上就是傻傻电源最近收集整理的关于redux-toolkit 在 react 中的基础用法的全部内容,更多相关redux-toolkit内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(72)

评论列表共有 0 条评论

立即
投稿
返回
顶部