Redux
一、什么是redux:
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。
二、安装与引入:
npm install --save redux //安装
import { createStore } from 'redux' //引入
三、redux三大原则:
单一数据源、state是只读的,使用纯函数来执行修改。
四、使用redux:
当安装好redux包并引入以后,我们通过creteStore(reducer)创建一个store状态机,用于状态管理。
reducer是一个纯函数【纯函数即返回值只有传入的参数决定】,reducer(state,action)有2个参数,state为当前的状态,action 就是一个描述“发生了什么”的普通对象,reducer中通过switch流程控制语句判断action.type的值,来更改state。
通过使用store.dispatch(action)来触发aciton的改变,然后通过store.subscribe(()=>{}),来手动订阅更新,当store.dispatch(action)后,就会触发store.subscribe。通过使用store.getState()获取当前store中state状态。
1、State:
- DomainState:服务端返回的State;
- UI State:关于当前组件的State;
- App State:全局的State;
2、Action事件:
- 本质就是一个JS对象;
- 必须包含type属性;
- 只是描述了有事情要发生,并没有描述如何去更新State;
3、 Reducer:
- 本质就是函数;
- 响应发送过来的Action;
- 函数接收两个参数,第一个是初始化State,另一个是发送过来的Action;
- 必须要有return返回值;
4、Store:
Store就是把action与reducer联系到一起的对象。
主要职责:
- 维持应用的state;
- 提供getState()方法获取state;
- 提供dispatch()方法发送action;
- 通过subscribe()方法注册监听;
- 通过subscribe()返回值注销监听。
Import {createStore} from ‘redux’
const store = createStore(传递的reducer)
五、使用:
项目结构如下:
1、创建一个Action:
- 在根目录下创建一个文件夹action,在action下创建一个index.js文件用来构建Action:const sendAction = () => {...} ;
- 在action创建函数里面利用return返回一个action对象,注意要携带type属性:const sendAction = () => {return {type: ‘send_action’, value: ‘发送了一个action’}}
- 把这个action创建函数进行导出:
module.exports = { sendAction }
代码:
const sendAction = () => {
return {
type: 'send_type',
value: '这是一个action'
}
}
module.exports = {
sendAction
}
2、创建一个Reducer:
- 在根目录下创建一个reducer目录,在reducer目录下创建一个index.js文件用来构建reducer,注意reducer要有两个接收函数:
const rootReducer = (state, action) => {...}
- 第一个参数是默认状态,我们可以定义一个初始化的state,然后进行赋值:
const initState = {value: ‘默认值’}
const rootReducer = (state=initState , action) => {...}
- 在函数里面判断第二个参数action的type值是否是我们需要发送的,如果是的话我们可以通过return返回新的state;
- 把reducer进行导出;
代码:
const initState = {
value: "默认值"
}
const reducer = (state = initState, action) => {
switch(action.type){
case "send_type":
return Object.assign({}, state, action) ;
default:
return state;
}
}
module.exports = {
reducer
}
3、构建Store:
- 在根目录下创建store文件夹,在store文件夹下创建index.js文件来构建store,注意createStore函数里面第一个参数是reducer:
import {createStore} from ‘redux’
// 在此导入创建的reducer
const store = createStore(reducer)
- createStore返回值就是我们创建的store;
- 将创建的store进行导出;
代码:
import {createStore} from 'redux'
import {reducer} from '../reducer'
const store = createStore(reducer);
export default store;
4、在组件中使用:
- 给组件ButtonCom.js中的button按钮绑定一个点击事件;
- 在组件一加载完毕的时候我们通过store来进行监听器的注册,返回值可以用来注销监听:
this.unSubscribe = store.subscribe(()=>{...})
- 在点击事件函数处理中通过store.dispatch来发送一个action:
handle = () => {
store.dispatch(sendAction());
}
代码:
import React from 'react'
import { Button } from 'antd';
import { RedoOutlined } from '@ant-design/icons';
import store from './store'
import {sendAction} from './action'
export default class MenuCom extends React.Component {
constructor(props) {
super(props);
//subscribe当store中数据发生变化就会更新数据
store.subscribe(function () {
this.setState({
storeValue: store.getState()
})
})
}
state = {
storeValue: store.getState()
}
/*onSubscribe = () => {
this.setState({
storeValue: store.getState()
})
}*/
componentWillMount = () => {
store.subscribe(this.onSubscribe)
}
handleClick = ()=>{
const action = sendAction();
store.dispatch(action);
}
render() {
return (<>
<Button type="primary" icon={<RedoOutlined/>} ghost onClick={()=>this.handleClick()}>修改</Button>
<div>{storeValue.value}</div>
</>)
}
}
再看简单的实例:
// npm i redux -S
// npm i react-redux -S
// package.json
// "dependencies": {
// "redux": "^5.0.1",
// "react-redux": "^9.1.2"
// },
// 1、src/redux/reducer.tsx
const initialState = { value: 0 }
type Action = {
type: string,
value?: any
}
function counterReducer(state = initialState, action: Action) {
if (action.type === 'counter/increment') {
return {
...state,
value: state.value + 1
}
}
return state
}
export default counterReducer
// 2、src/redux/index.tsx
import { createStore } from "redux"
import reducer from "./reducer"
const store = createStore(reducer)
export default store
// 3、src/index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import reportWebVitals from "@/reportWebVitals";
import { Provider } from 'react-redux'
import store from '@/redux'
import App from "./App";
import "@/index.css";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
);
reportWebVitals();
// 4、src/pages/home/index.tsx
import React, { useState } from "react";
import { Timeline, Button } from "antd";
import store from "@/redux";
type Obj = {
value: number
}
const App: React.FC = () => {
const [obj, setObj] = useState<Obj>({value: 0});
// 监听Action,数据更新会执行
store.subscribe(() => {
setObj(store.getState());
});
const increate = () => {
store.dispatch({ type: "counter/increment" });
console.log(store.getState());
};
return (
<>
<div>{obj.value}</div>
<div>
<Button type="primary" onClick={increate}>
Redux实例
</Button>
</div>
</>
);
};
export default App;
六、总结:
完整示例:https://github.com/samveduan/redux.git
Redux Toolkit
redux-toolkit 是官方推荐的编写redux逻辑的方法,简化了redux的配置过程,无需再创建actions、reducer的,更大程度方便使用redux仓库。
Redux Toolkit 包含了 Redux 核心,以及我们认为对于构建 Redux 应用程序必不可少的其他关键软件包(例如 Redux Thunk 和 Reselect)。
在使用redux-toolkit的时候需要搭配React 绑定 和 开发者工具:
npm install react-redux
npm install --save-dev redux-devtools
redux-toolkit是Redux 5.x版本新增的,我们对比下Redux和Redux Toolkit写法:
Redux:
import { createStore } from 'redux'
/**
* 这是一个 reducer 函数:接受当前 state 值和描述“发生了什么”的 action 对象,它返回一个新的 state 值。
* reducer 函数签名是 : (state, action) => newState
*
* Redux state 应该只包含普通的 JS 对象、数组和原语。
* 根状态值通常是一个对象。 重要的是,不应该改变 state 对象,而是在 state 发生变化时返回一个新对象。
*
* 你可以在 reducer 中使用任何条件逻辑。 在这个例子中,我们使用了 switch 语句,但这不是必需的。
*
*/
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
// 创建一个包含应用程序 state 的 Redux store。
// 它的 API 有 { subscribe, dispatch, getState }.
let store = createStore(counterReducer)
// 你可以使用 subscribe() 来更新 UI 以响应 state 的更改。
// 通常你会使用视图绑定库(例如 React Redux)而不是直接使用 subscribe()。
// 可能还有其他用例对 subscribe 也有帮助。
store.subscribe(() => console.log(store.getState()))
// 改变内部状态的唯一方法是 dispatch 一个 action。
// 这些 action 可以被序列化、记录或存储,然后再重放。
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}
Redux Toolkit:
Redux Toolkit 简化了编写 Redux 逻辑和设置 store 的过程。 使用 Redux Toolkit,相同的逻辑如下所示:
import { createSlice, configureStore } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
incremented: state => {
// Redux Toolkit 允许在 reducers 中编写 "mutating" 逻辑。
// 它实际上并没有改变 state,因为使用的是 Immer 库,检测到“草稿 state”的变化并产生一个全新的
// 基于这些更改的不可变的 state。
state.value += 1
},
decremented: state => {
state.value -= 1
}
}
})
export const { incremented, decremented } = counterSlice.actions
const store = configureStore({
reducer: counterSlice.reducer
})
// 可以订阅 store
store.subscribe(() => console.log(store.getState()))
// 将我们所创建的 action 对象传递给 `dispatch`
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}
一个小实例:
1、安装:
npm i @reduxjs/toolkit react-redux
2、src/redux/reducer.tsx
同步配置
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
export interface CounterState {
value: number
}
const initialState: CounterState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state:CounterState) => {
state.value += 1
},
decrement: (state:CounterState) => {
state.value -= 1
},
},
})
// 这里的actions对应的是上面的reducers function
export const { increment, decrement } = counterSlice.actions
export default counterSlice.reducer
异步配置
import { createSlice,createAsyncThunk } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
export interface CounterState {
value: number
}
const initialState: CounterState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
// 同步操作
reducers: {
......
},
// 异步操作
extraReducers(builder){
// 这里的payload就是incrementAsync的返回值
builder.addCase(incrementAsync.fulfilled,(state,{payload})=>{
state.value += payload
})
}
})
// 定义异步函数
export const incrementAsync = createAsyncThunk(
"incrementAsync",
async(p:number)=>{
const res = await new Promise<number>(r=>{
setTimeout(() => {
r(p)
}, 1000);
})
return res
})
export const { incrementAsync } = counterSlice.actions
export default counterSlice.reducer
3、src/redux/index.tsx中注册
import { configureStore } from "@reduxjs/toolkit";
// 引入counterSlice.ts
import counterStore from "@/redux/reducer";
const store = configureStore({
reducer:{
// 注册,注意名字要与counterSlice.ts中的name一致
counter:counterStore
}
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export default store
4、src/index.tsx中注册store
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import reportWebVitals from "@/reportWebVitals";
import { Provider } from 'react-redux'
import store from '@/redux'
import App from "./App";
import "@/index.css";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
);
reportWebVitals();
5、使用:/src/pages/app.tsx
import React from "react";
// useSelect用于选择操作哪个store
// useDispatch用于生成实例,该实例可以调用reducers的function
import { useSelector, useDispatch } from "react-redux"
// 引入index中的RootState类型 js项目不需要
import { AppDispatch, RootState } from "@/redux/"
// 引入functions
import { decrement, increment } from "@/redux/reducer"
const App: React.FC = () => {
const count = useSelector((state: RootState) => state.counter.value)
const dispatch: AppDispatch = useDispatch() // 异步函数必须加AppDispatch类型,否则报错,同步可以不添加
return (
<>
<div>{count}</div>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(decrement())}>-1</button>
</>
)
};
export default App;
简化写法:
1、src/redux/index.tsx
import { createSlice, configureStore } from "@reduxjs/toolkit";
export interface counterState {
value: number;
}
const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0
},
reducers: {
incremented: (state: counterState) => {
state.value += 1;
},
decremented: (state: counterState) => {
state.value -= 1;
}
}
});
export const { incremented, decremented } = counterSlice.actions;
const store = configureStore({
reducer: counterSlice.reducer
});
export default store;
2、src/index.tsx中注册store
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import reportWebVitals from "@/reportWebVitals";
import { Provider } from 'react-redux'
import store from '@/redux'
import App from "./App";
import "@/index.css";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
);
reportWebVitals();
3、使用:/src/pages/app.tsx
import React, {useState} from "react";
import store, { incremented, decremented } from "@/redux/"
const App: React.FC = () => {
const [value, setValue] = useState(0)
store.subscribe(() => setValue(store.getState().value))
return (
<>
<div>{value}</div>
<button onClick={() => store.dispatch(incremented())}>+1</button>
<button onClick={() => store.dispatch(decremented())}>-1</button>
</>
)
};
export default App;
Mobx:
Mobx的使用方法:Mobx的使用方法_mobx使用步骤-CSDN博客