likes
comments
collection
share

16_Redux系列

作者站长头像
站长
· 阅读数 29

什么是纯函数?

函数式编程中有一个非常重要的概念叫**纯函数,**纯函数的概念:

  • 函数在获得相同的输入时,会产生相同的输出
  • 函数的输出和输入值以外的其它状态无关,也和由I/O设备产生的外部输出无关
  • 函数**不能有语义上可观察到的函数副作用,**如:事件触发、更改输出值以外的内容

总结:

  1. 确定的输入,一定会有确定的输出
  2. 函数在执行过程中,不会产生副作用

比如:

  • 数组的slice方法不会修改原数组,而是生成一个新数组,所以slice就是一个纯函数
  • splice方法会返回一个新数组,并且会修改原数组,所以splice方法不是一个纯函数

在React中组件被要求是一个纯函数(为什么是,因为还有class组件),Redux中的reducer要求必须是一个纯函数。

什么是副作用?

副作用是相对于主做用来说的,一个函数除了主作用,其他作用就是副作用。在执行一个函数式,除了返回函数值之外,还对做了其他的事情(副作用),比如修改了参数、外部变量等。常见的副作用:

  • 数据请求ajax发送
  • 手动修改dom
  • localStorage操作 等

副作用经常会导致bug的出现!对于React组件来说,主做用就是根据数据(state/props)渲染UI,除此之外都是副作用。

Redux介绍

16_Redux系列Redux 是 JavaScript 状态容器,提供可预测化的状态管理可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。Redux 经常和 React 一起使用,但是Redux和React并没有直接的关系,完全可以其它界面库中使用。 它体小精悍(只有2kB,包括依赖)。

为什么需要redux?

现在的前端应用已经变得越来越复杂,需要管理状态越来越多、越来越复杂;这些状态包括服务器返回的数据、用户操作产生的数据、缓存数据等,还有一些UI的状态,比如元素的选中样式,加载动画,分页状态;管理这些不断变化的状态是非常困难的,因为状态之间可能会相互依赖,状态变化时页面可能也会需要更新;当应用非常复杂时,这些状态在什么时候、因为什么、发生了怎样的变化是很难控制和追踪的;React本身的工作是根据state或者props帮助我们解决DOM的渲染过程,但是组件自己的state、组件通信时的props、context共享数据都是需要我们自己管理的。所以需要一个帮助我们管理状态的容器Redux,Redux 是 JavaScript 状态容器,提供可预测化的状态管理。

Redux作者的建议

  • UI相关的组件内部可以维护的状态,在组件内部自己来维护;
  • 大部分需要共享的状态,都交给redux来管理和维护;
  • 从服务器请求的数据(包括请求的操作),交给redux来维护;

Redux的三大原则

单一数据源:

  • 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
  • Redux并没有强制不能创建多个store,但是多个store不利于维护
  • 单一的数据源让整个应用的state变得方便修改、追踪和维护

State是只读的:

  • 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
  • 这就确保了视图和网络请求都不能直接修改 state,只能通过action来表达自己想要如何修改state
  • 因为所有的修改都被集中化处理,且严格按照顺序执行,因此不用担心 race condition (竞态)的出现

使用纯函数来执行修改:

  • 为了描述 action 如何改变 state tree ,你需要编写 reducers。
  • Reducer 只是一些纯函数,它将旧 state 和 action 联系在一起,返回新的 state
  • 随着应用变大,可以把reducers拆成多个小的 reducer,分别独立地操作 state tree 的不同部分
  • 所有的reducer都应该是纯函数,不能产生任何副作用

Redux核心概念-Store

Store 是把action和reducers联系到一起的对象。Store 有以下职责:

  • 维护应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器。

Redux核心概念-action

Redux要求必须通过action来更新数据,actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state

  • 所有数据变化,必须使用dispatch(派发) action 来更新
  • action是一个普通的JavaScript对象,用来描述这次更新的type和content
  • 强制使用action的好处是可以清晰的知道数据到底发生了什么变化,所有的变化都是可追踪、可预测的

Redux核心概念-reducer

Reducers 描述了action如何修改 state:

  • reducer 是一个纯函数
  • 它将旧 state 和 action 联系在一起,返回新的 state

Redux的基本使用

安装:npm install redux --save

const { createStore } = require("redux")

// 初始化的数据
const initialState = {
  name: "jay",
  age: 100
}

// 定义reducer函数: 纯函数
// 两个参数: 
// 参数一: store中目前保存的state
// 参数二: 本次需要更新的action(dispatch传入的action)
// 返回值: 它的返回值会作为store之后存储的state
function reducer(state = initialState, action) {
  switch (action.type) {
    case "change_name":
      return { ...state, name: action.name }
    case "add_age":
      return { ...state, age: state.age + action.age }
    default:
      // 默认需要返回原始的state
      return state
  }
}

// 多个reducer合并
// const reducer = combineReducers({
//   user: userReducer,
//   theme: themeReducer
// })

// 创建的store
const store = createStore(reducer)

// 获取state
// console.log('获取user state中的name:', store.getState().user.name)
console.log('获取state:', store.getState())

// 订阅state的变化
const unsubscribe = store.subscribe(() => {
  console.log('订阅state的变化:', store.getState())
})

// 通过action修改state
const changeNameAction = { type: 'change_name', name: 'jay chou' }
store.dispatch(changeNameAction)

// 取消订阅
unsubscribe()

// 通过action修改state
const changeAgeAction = { type: 'change_name', age: 50 }
store.dispatch(changeAgeAction)

在React项目中使用Redux

Redux使用流程图

16_Redux系列

代码拆分

在实际的项目开发中,如果将所有的代码放到同一个文件中,那么在redux很复杂时,代码会变的难以维护。创建一个store文件夹,将代码按照 store(index.js)、reducer(reducer.js)、action(actionCreator.js)、actionTypes(actionTypes.js)拆分为一个个单独文件。

store/index.js

import { createStore } from "redux"
import reducer from "./reducer"

// 多个reducer合并
// const reducer = combineReducers({
//   user: userReducer,
//   theme: themeReducer
// })

// 创建的store
const store = createStore(reducer)

export default store

store/reducer.js

// 导入常量
import { CHANGE_NAME, CHANGE_AGE } from "./actionTypes"

// 初始化的数据
const initialState = {
  name: "jay",
  age: 44
}

function reducer(state = initialState, action) {
  switch(action.type) {
    case CHANGE_NAME:
      return { ...state, name: action.name }
    case CHANGE_AGE:
      return { ...state, age: state.age + action.age }
    default:
      return state
  }
}

export default reducer

store/actionCreators.js

// 导入常量
import { CHANGE_NAME, CHANGE_AGE } from "./actionTypes"

export const changeNameAction = (name) => ({
  type: CHANGE_NAME,
  name
})

export const changeAgeAction = (age) => ({
  type: CHANGE_AGE,
  age
})

store/actionTypes.js

export const CHANGE_NAME = "change_name"
export const CHANGE_AGE = "change_age"

在组件中使用

  1. 需要在componentDidMount中订阅store的变化,当数据变化时将最新的数据设置到组件的state
  2. 点击按钮时,调用store的dispatch派发修改数据的action
import React, { PureComponent } from 'react'
import store from './store'
import {changeNameAction} from './store/actionCreators'

export class App extends PureComponent {
  constructor() {
    super()

    this.state = {
      name: store.getState().name
    }
  }

  componentDidMount() {
    this.unsubscribe = store.subscribe(() => {
      console.log('store更新了')
      const name = store.getState().name
      this.setState({ name })
    })
  }

  componentWillUnmount() {
    this.unsubscribe()
  }

  render() {
    const { name } = this.state

    return (
      <div>
        <h2>用户名: {name}</h2>
        <button onClick={() => store.dispatch(changeNameAction("Jay Chou"))}>修改name</button>
      </div>
    )
  }
}

export default App

react-redux

介绍

React Redux是Redux的官方React UI绑定工具。它允许React组件从Redux存储读取数据,并将操作分派到存储以更新状态。 具有高效、灵活的特性。安装:npm install react-redux --save

Provider

redux-react提供了一个Provider组件,将其设置到所有组件的最外层,再将创建的store设置为Provider组件的store属性,可以使应用中的所有组件都能访问到Redux中的数据。

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>` 组件包裹 `<App>` 组件
  // 并把 Redux store 作为 prop 传入
  <Provider store={store}>
    <App />
  </Provider>
);

Connect

React Redux提供了一个连接函数,用于从Redux存储中读取值(并在存储更新时重新读取值)。connect函数接受两个参数,这两个参数都是可选的:

  • mapStateToProps:每次store state更改时调用。它接收整个store state,将该组件需要的数据映射到组件的props。
    • 没传该参数,组件不会在store发生变化时重新渲染
  • mapDispatchToProps:此参数可以是函数,也可以是对象。
    • 如果它是一个函数,它将在创建组件时被调用一次。它接收dispatch作为参数,并且应该返回一个对象,该对象包含使用dispatch来调度操作的函数。
    • 如果它是一个充满action creators的对象,那么每个action creator 都会被映射到组件的props,在调用时自动dispatch 对应的 action。注意:建议使用“对象简写”形式
    • 如果没有传该参数,可使用 props.dispatch 在组件中获取dispatch
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { changeNameAction, changeAgeAction } from './store/actionCreators'

export class App extends PureComponent {

  render() {
    console.log('render')
    const { name, age, changeName, changeAge } = this.props

    return (
      <div>
        <h2>用户名: {name}-age: {age}</h2>
        <button onClick={() => changeName("Jay Chou")}>修改name</button>
        <button onClick={() => changeAge(55)}>修改age</button>
      </div>
    )
  }
}

const mapStateToProps = (state) => ({
  name: state.name,
  age: state.age,
})

// mapDispatchToProps是一个函数
// const mapDispatchToProps = (dispatch) => ({
//   changeName(name) {
//     dispatch(changeNameAction(name))
//   },
//   changeAge(age) {
//     dispatch(changeAgeAction(age))
//   }
// })

// mapDispatchToProps是一个对象
const mapDispatchToProps = {
  changeName: changeNameAction,
  changeAge: changeAgeAction
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

useSelector和useDispatch

在 React 函数组件中通过useSelector获取state,通过useDispatch获取dispatch方法。useSelector的作用从Store中获取state:

  • 参数一:selector函数,selector 函数接收 Redux store 的 state 作为其参数,然后从 state 中取值并返回
  • 参数二:可以通过比较来决定组件是否重新渲染
    • **useSelector 使用严格的 === 来比较结果,因此只要 selector 函数返回的结果是新地址引用,组件就会重新渲染!**这意味着如果在 selector 中创建并返回新地址引用,那么每次 dispatch action 后组件都会被重新渲染,即使数据值确实没有改变。
    • React-Redux 有一个 **shallowEqual **比较函数,我们可以使用它来检查数组 内部每一项 是否仍然相同

useDispatch调用后会返回dispatch函数,保存后可以在组件中直接使用;

import React, { memo } from 'react'
import { useSelector, useDispatch, shallowEqual } from "react-redux";
import { changeNameAction, changeAgeAction } from './store/actionCreators'

const App = memo(() => {
  const {name, age} = useSelector(state => ({
    name: state.name,
    age: state.age
  }), shallowEqual)
  const dispatch = useDispatch()

  const changeName = (name) => {
    dispatch(changeNameAction(name))
  }

  const changeAge = (age) => {
    dispatch(changeAgeAction(age))
  }
  
  return (
    <div>
      <h2>用户名: {name}-age: {age}</h2>
      <button onClick={() => changeName("Jay Chou")}>修改name</button>
      <button onClick={() => changeAge(55)}>修改age</button>
    </div>
  )
})

export default App

中间件Middleware

理解中间件

在前面的代码中都是一些同步操作,每当 dispatch action 时,state 会被立即更新。在真实的开发过程中,Redux中保存的数据可能来自服务器。在React中需要在class组件的componentDidMount生命周期、或者useEffect中发送网络请求获取数据,再将数据保存到Redux中。但是在Redux中进行异步操作标准的做法是使用 Redux Thunk 中间件。要引入 redux-thunk 这个专门的库才能使用。在 Express 或者 Koa 等服务端框中,middleware 是指可以被嵌入在框架接收请求到产生响应过程之中的代码。例如,Express 或者 Koa 的 middleware 可以完成添加 CORS headers、记录日志、内容压缩等工作。middleware 最优秀的特性就是可以被链式组合。可以在一个项目中使用多个独立的第三方 middleware。Redux 中间件被用于解决不同的问题,但其中的概念是类似的:

  • Redux中间件的目的是在dispatch的action和最终到达的reducer之间,扩展一些功能
  • 比如:日志记录、调用异步接口、创建崩溃报告、或者路由等等。

redux-thunk

  • 默认情况 dispatch(action对象), action需要是一个JavaScript对象
  • redux-thunk可以让dispatch(action函数),action可以是一个函数
  • 该函数会被调用,并且会传给这个函数一个dispatch函数和getState函数
    • dispatch函数:用于派发action
    • getState函数:之后的一些操作可能需要原来的状态,可以获取之前的一些状态
  1. 安装:npm install redux-thunk --save
  2. 使用:
  • 使用applyMiddleware结合多个Middleware,返回一个enhancer函数
  • 将enhancer作为第二个参数传入到createStore中
import { createStore, applyMiddleware } from "redux"
import thunk from "redux-thunk"
import reducer from "./reducer"

const enhancer = applyMiddleware(thunk)
const store = createStore(reducer, enhancer)
  1. 定义一个返回函数的action
export const fetchUserInfoAction = () => {
  return function (dispatch, getState) {
    // console.log("之前的state:", getState())
    // 异步操作: 网络请求
		fetch("http://xxx.xxx.xxx.xxx:8000/userInfo")
    .then(res => res.json())
    .then(res => {
			const { name, age } = res.data
      // 派发修改数据的action
      dispatch(changeNameAction(name))
      dispatch(changeAgeAction(age))
    })
  }
}

4.在组件中使用

import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { fetchUserInfoAction } from './store/features/user'

export class App extends PureComponent {
	componentDidMount() {
		// 调用
		this.props.fetchUserInfo()
	}

  render() {
    console.log('render')
    const { name, age } = this.props

    return (
      <div>
        <h2>用户名: {name}-age: {age}</h2>
      </div>
    )
  }
}

const mapStateToProps = (state) => ({
  name: state.name,
  age: state.age,
})

// const mapDispatchToProps = (dispatch) => ({
// 	fetchUserInfo() {
// 		dispatch(fetchUserInfoAction())
// 	}
// })

const mapDispatchToProps = {
  fetchUserInfo: fetchUserInfoAction,
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

自定义中间件

function log(store) {
  const next = store.dispatch

  function logDispatch(action) {
    console.log("当前派发的action:", action)
    // 真正派发的代码: 使用之前的dispatch进行派发
    next(action)
    console.log("派发之后的结果:", store.getState())
  }

  // monkey patch: 猴补丁 => 篡改现有的代码, 对整体的执行逻辑进行修改
  store.dispatch = logDispatch
}

function thunk(store) {
  const next = store.dispatch
  function dispatchThunk(action) {
    if (typeof action === "function") {
      action(store.dispatch, store.getState)
    } else {
      next(action)
    }
  }
  store.dispatch = dispatchThunk
}

function applyMiddleware(store, ...fns) {
  fns.forEach(fn => fn(store))
}

// 调用applyMiddleware
applyMiddleware(store, log, thunk)

Redux Tookit(RTK)

Redux Toolkit(简称RTK) 是 Redux 官方强烈推荐,开箱即用的一个高效的 Redux 开发工具集。它旨在成为标准的 Redux 逻辑开发模式。RTK可以帮助我们处理使用Redux过程中的重复性工作,简化Redux中的各种操作。安装:npm install @reduxjs/toolkit react-redux

createSlice

createSlice是一个全自动的创建reducer切片的方法。createSlice需要一个对象作为参数,对象中通过不同的属性来指定reducer的配置信息。const slice = createSlice(configuration object)配置对象中的属性:

  • name —— reducer的名字,会作为action中type属性的前缀,不要重复
    • 在redux-devtool中会显示对应的name
  • initialState —— state的初始值
  • reducers —— reducer的具体方法,RTK会根据这些方法自动生成action对象,这些action对象会存储到切片对象actions属性中
    • 对象类型,并且可以添加很多函数,每个函数相当于原来reducer中switch中的一个case语句
    • 参数一:state
    • 参数二:action对象 {type:'name/xxx', payload:...}

返回值是一个对象,里面包含自动生成的 reduceractions(actionCreator组成的对象)。

import { createSlice } from "@reduxjs/toolkit"

const userSlice = createSlice({
  name: "user",
  initialState: {
    name: jay,
    age: 41,
  },
  reducers: {
    changeName(state, { payload }) {
      state.name = payload
    },
    changeAge(state, { payload }) {
      state.age = payload
    }
  }
})

export const {changeName, changeAge} = userSlice.actions
export default userSlice.reducer

configureStore

configureStore用于创建store对象,参数是一个对象,在这个对象中可以通过不同的属性来对store进行设置:

  • reducer:将createSlice返回的对象的reducer属性组成一个对象作为数据
  • middleware:可选参数,传入中间件
  • devTools:可选参数,是否配置devTools工具,默认为true
import { configureStore } from "@reduxjs/toolkit"
import userReducer from "./features/userSlice"
// import themeReducer from "./features/homeSlcie"

const store = configureStore({
  reducer: {
    user: userReducer,
    // home: homeReducer
  }
})

export default store

createAsyncThunk

之前需要使用redux-thunk中间件才可以在redux中进行异步操作。Redux Toolkit默认集成了thunk的功能:createAsyncThunk接收一个** action type 字符串一个返回值为 promise 的函数**, 并生成一个 thunk 函数,这个 thunk 函数可以基于之前那个 promise ,dispatch 一组 type 为 pending/fulfilled/rejected 的 action:

  • pending:action被发出,但是还没有最终的结果
  • fulfilled:获取到最终的结果(有返回的结果)
  • rejected:执行过程中有错误或者抛出了异常

也可以在createSliceentraReducer中监听这些结果。

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'

export const fetchUserInfoAction = createAsyncThunk(
	"fetchUserInfo",
	async (extraInfo, { dispatch, getState }) => {
		// console.log(extraInfo, dispatch, getState)
		const res = await fetch("http://xxx.xxx.xxx.xxx:8000/userInfo")
		const resJson = await res.json()
		const { name, age } = resJson.data
		dispatch(changeName(name))
		dispatch(changeAge(age))
		// return resJson
	}
)

const userSlice = createSlice({
	name: "user",
	initialState: {
		name: jay,
		age: 41,
	},
	reducers: {
		changeName(state, { payload }) {
			state.name = payload
		},
		changeAge(state, { payload }) {
			state.age = payload
		}
	}
	// extraReducers的第一种写法
	// extraReducers: {
	//   [fetchUserInfoAction.pending](state, action) {
	//     console.log("fetchUserInfoAction pending")
	//   },
	//   [fetchUserInfoAction.fulfilled](state, { payload }) {
	//     console.log("fetchUserInfoAction fulfilled")
	//     // const { name, age } = payload.data
	//     // state.name = name
	//     // state.age = age
	//   },
	//   [fetchUserInfoAction.rejected](state, action) {
	//     console.log("fetchUserInfoAction rejected")
	//   }
	// },
	// extraReducers的第二种写法
	// extraReducers: (builder) => {
		// builder.addCase(fetchUserInfoAction.pending, (state, action) => {
		//   console.log("fetchUserInfoAction pending")
		// }).addCase(fetchUserInfoAction.fulfilled, (state, { payload }) => {
		//     // const { name, age } = payload.data
		//     // state.name = name
		//     // state.age = age
		// })
	// }
})

export const { changeName, changeAge } = userSlice.actions
export default userSlice.reducer

在组件中使用

import React, { PureComponent } from 'react'
import { connect } from "react-redux"
import { fetchUserInfoAction } from './store/features/userSlice'

export class Home extends PureComponent {
	componentDidMount() {
		this.props.fetchHomeMultidata()
	}

	render() {
		console.log('render')
		const { name, age } = this.props

		return (
			<div>
				<h2>用户名: {name}-age: {age}</h2>
			</div>
		)
	}
}

const mapStateToProps = (state) => ({
	name: state.user.name,
	age: state.user.age,
})

const mapDispatchToProps = (dispatch) => ({
	fetchUserInfo() {
		dispatch(fetchUserInfoAction())
	}
})

export default connect(mapStateToProps, mapDispatchToProps)(App)

数据不可变性

在React中开发中,总会强调数据的不可变性

  • 无论是类组件中的state,还是redux中管理的state
  • 事实上整个JavaScript编码过程中,数据的不可变性都是非常重要的’

我们经常会通过浅拷贝来完成某些操作,但是浅拷贝事实上也是存在问题的:

  • 比如:过大的对象,进行浅拷贝也会造成性能的浪费
  • 比如:浅拷贝后的对象,在深层改变时,依然会对之前的对象产生影响

事实上Redux Toolkit底层使用了immerjs的一个库来保证数据的不可变性为了节约内存,又出现了一个新的算法:Persistent Data Structure(持久化数据结构或一致性数据结构)

  • 用一种数据结构来保存数据
  • 当数据被修改时,会返回一个对象,但是新的对象会尽可能的利用之前的数据结构而不会对内存造成浪费

Redux Tookit Query(RTKQ)

RTKQ是一种功能强大的数据获取和缓存工具,它已经集成在了RTK中,默认使用简单封装过得fetch发送请求。它旨在简化在web应用程序中加载数据的常见情况,无需自己手工编写数据获取和缓存逻辑。Web应用中加载数据时需要处理的问题:

  1. 根据不同的加载状态显示不同UI组件
  2. 减少对相同数据重复发送请求
  3. 使用乐观更新,提升用户体验
  4. 在用户与UI交互时,管理缓存的生命周期

这些问题RTKQ都可以帮助我们处理。首先,可以直接通过RTKQ向服务器发送请求加载数据,并且RTKQ会自动对数据进行缓存,避免重复发送不必要的请求。其次,RTKQ在发送请求时会根据请求不同的状态返回不同的值,可以通过这些值来监视请求发送的过程并随时中止。

创建一个API Slice

createApi需要一个对象作为参数,常用属性如下,更多属性可查看官网

  • reducerPath:Api的唯一标识,主要用来创建store时指定action的type属性,如果不指定默认为api
  • baseQuery:用来设置发送请求的工具,就是用什么发请求(点击),RTKQ提供了fetchBaseQuery作为查询工具,它对fetch进行了简单的封装
    • fetchBaseQuery:简单封装过的fetch调用后会返回一个封装后的工具函数。需要一个配置对象作为参数,baseUrl表示Api请求的基本路径,指定后请求将会以该路径为基本路径。更多属性查看官网
  • tagTypes: 字符串标记类型名称的数组。指定标记类型是可选的,但您应该定义它们,以便它们可以用于缓存和无效。定义标记类型时,您将能够为它们提供providesTag,并在配置终结点时使用invalidatesTag使其无效。
  • endpoints:endpoints是一个回调函数。回调函数接收一个build对象,使用build对象对点进行映射。回调函数的返回值是一个对象,Api对象中的所有端点都要在该对象中进行配置。
    • 返回值对象的属性名就是要实现的功能名,比如获取用户信息getUserInfo
    • 查询方法:build.query() 参数是一个对象,常用属性如下
      • query():函数,接收参数,需要返回一个string(请求子路径)或者object(请求的配置对象包含url、method、headers、body、params等)
      • transformResponse(baseQueryReturnValue, meta, arg):用来转换相应数据的格式
      • keepUnusedDataFor:数据缓存的时间,单位秒
      • providesTags:确定哪个“标记”附加到查询返回的缓存数据。应为标记类型字符串的数组、带id的标记类型对象的数组,或返回此类数组的函数。
    • 增删改:build.mutation()
      • invalidatesTags:确定应重新提取或从缓存中删除哪些缓存数据。应该和providesTag相同。
    • 钩子函数名字为useXxxQuery或useXxxMutation,中间的Xxx就是方法名
/* React-specific entry point that automatically generates
   hooks corresponding to the defined endpoints */
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
// import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/dist/query/react";

const userApi = createApi({
  reducerPath: 'userApi',
  // 指定查询的基础信息,发送请求使用的工具
  baseQuery: fetchBaseQuery({
    baseUrl: 'http://localhost:8000'
  }),
  // endpoints 用来指定Api中的各种功能,是一个方法,需要一个对象作为返回值
  endpoints(build) {
    // build是请求的构建器,通过build来设置请求的相关信息
    return {
      getUserInfo: build.query({
        query() {
          return '/home/multidata'
        }
      })
    }
  }
})

// Api对象创建后,对象中会根据各种方法自动的生成对应的钩子函数
// 通过这些钩子函数,可以来向服务器发送请求
// 钩子函数的命名规则 getStudents --> useGetStudentsQuery
export const {useGetUserInfoQuery} = userApi
export default userApi

配置到Store中

  1. 添加createApi生成的reducer到store
  2. 添加自动生成的中间件,中间件用来实现缓存、轮询等
import { configureStore } from "@reduxjs/toolkit"
import userReducer from "./features/userSlice"
// import themeReducer from "./features/homeSlcie"
import userApi from "./api/userApi"

const store = configureStore({
	reducer: {
		user: userReducer,
		// home: homeReducer
		[userApi.reducerPath]: userApi.reducer
	},
	// 添加api中间件
	middleware: (getDefaultMiddleware) =>
		getDefaultMiddleware().concat(userApi.middleware),
})

export default store

在函数式组件中使用query hooks

hook返回的对象中包含了以下属性:

  • data:最新返回的数据
  • currentData:当前的数据
  • error:错误信息
  • isUninitialized:如果为true则表示查询还没开始
  • isLoading:为true时,表示请求正在第一次加载
  • isFetching:为true时,表示请求正在加载
  • isSuccess:为true时,表示请求发送成功
  • isError:为true时,表示请求有错误
  • refetch:函数,用来重新加载数据

useXxxQuery的第一个参数是传入的数据;useXxxQuery可以接收一个对象作为第二个参数,通过该对象可以对请求进行配置:

  • selectFromResult:指定返回的结果,对返回的结果进行筛选
  • pollingInterval:设置轮询的间隔,单位毫秒 如果为0则表示不轮询
  • skip:是否跳过当前请求,默认false
  • refetchOnMountOrArgChange:是否每次都重新加载数据 false正常使用缓存,true每次都重载数据,数字表示数据缓存的时间(秒)
  • refetchOnFocus:是否在重新获取焦点时重载数据
  • refetchOnReconnect:是否在重新连接后重载数据
import React, { memo } from 'react'
import { useGetUserInfoQuery } from '../store/api/homeApi'

const Home = memo(() => {
  // 调用api中的钩子获取数据
  // 返回值是一个对象,请求过程中相关数据都在该对象中
  const {data, isSuccess, isLoading} = useGetUserInfoQuery();
  const {name, age} = data.data

  return (
    <div>
      {isLoading && <h2>加载中....</h2>}
      {isSuccess && <h2>成功!</h2>}
      <h2>用户名: {name}-age: {age}</h2>
    </div>
  )
})

export default Home