在React中使用Redux
React项目中,使用Redux的主要原因和优点
- 单一数据源和状态管理:Redux提供了一个全局的、单一的状态树来管理整个应用的状态。这使得状态的管理更加集中和一致,减少了组件之间通过props进行状态传递的复杂性。React本质上是一个UI库,其数据流是单向的,即从父组件流向子组件。当项目中的数据变得复杂时,Redux可以帮助更好地管理这些数据。
- 可预测和可维护的状态变化:Redux强制使用纯函数(即reducer)来更新状态,这确保了状态变化的可预测性和可追踪性。由于状态的变化只能通过reducer进行,并且每次变化都会生成一个新的状态对象,因此可以轻松地跟踪和调试状态的变化。此外,这种强制性的更新方式也使得代码更易于维护和测试。
- 跨组件状态同步:Redux允许组件在不同层次结构中共享状态,并在状态变化时得到通知。这在需要实时反应全局状态变化的场景中非常有用,如实时通知、用户登录状态等。
- 中间件支持:Redux提供了中间件机制,允许在状态更新的过程中执行额外的逻辑。这使得Redux能够很好地处理异步操作,如API调用、数据获取等。通过结合如Redux Thunk、Redux Saga等中间件,异步操作变得更加可控和可测试。
- 时间旅行和调试:Redux通过在状态更改历史中保留每个动作的快照,使得可以轻松地进行时间旅行式的调试。这意味着可以回放应用状态的历史,以便找到错误或理解应用程序行为。
- 社区与生态圈:Redux拥有庞大的社区和丰富的生态圈,这为其提供了大量的支持和资源。这使得Redux在开发过程中更容易找到解决方案和最佳实践。
使用
下载依赖包
npm i react-redux @reduxjs/toolkit
假设我们这边需要设置一个count值存储在redux中 使用按钮去修改它的值
1.首先我们需要在模块中创建一个文件 例如 /src/store/moudle/counterSlice.jsx
import {createSlice} from "@reduxjs/toolkit";
const counterSlice = createSlice({
//必须要给这个文件起一个名字
name: "counter",
initialState: {
count: 0, //计数字段
},
//同步数据信息
reducers: {
// 不传入参数
increment(state) {
state.count += 1;
},
decrement(state) {
if (state.count <= 0) {
state.count = 0;
return
}
state.count -= 1;
},
// 传入参数
incrementByAmount(state, action) {
state.count += action.payload;
},
decrementByAmount(state, action) {
state.count -= action.payload
if (state.count <= 0) {
state.count = 0;
}
},
},
})
export const {increment, decrement, incrementByAmount, decrementByAmount} = counterSlice.actions;
export default counterSlice.reducer;
2.然后我们创建store管理注册仓库 文件创建在/src/store/index.jsx文件中
import { configureStore } from '@reduxjs/toolkit';
import countReducer from "./module/counterSlice";
const store =configureStore({
reducer: {
counter: countReducer, // counter 这个名字不能乱取 因为后面取值就要靠这个来取
},
});
export default store;
3.其次就是我们使用了 文件放在/src/page/count/index.jsx
import {Button} from "antd";
import {increment, decrement, incrementByAmount, decrementByAmount} from '../../store/module/counterSlice'
import {useDispatch, useSelector} from "react-redux";
import {PlusOutlined, MinusOutlined} from "@ant-design/icons";
function CountIndex() {
// 导入并使用他 获取dispatch 去执行redux中的方法
const dispatch = useDispatch()
// 取出存入Redux中的数据信息 counter就是在store注册的时候起的名字
const count = useSelector(state => state.counter.count);
return (
<div>
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
<Button type="primary" ghost icon={<PlusOutlined/>} onClick={() => dispatch(increment())}></Button>
<Button type="primary" ghost style={{marginLeft: 10}}
onClick={() => dispatch(incrementByAmount(10))}>加10</Button>
<h3 style={{color: "green"}}>{count}</h3>
<Button danger ghost icon={<MinusOutlined/>} onClick={() => dispatch(decrement())}></Button>
<Button danger ghost style={{marginLeft: 10}}
onClick={() => dispatch(decrementByAmount(10))}>减10</Button>
</div>
</div>
)
}
export default CountIndex
3.在index.js中注册
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import store from './store';
import {Provider} from 'react-redux';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App/>
</Provider>
);
reportWebVitals();
4.效果图
异步获取数据信息
5.这里都是我们的同步数据 如果我们需要异步请求获取数据的话 需要这个写
// 异步获取数据信息
//getDataInfo是你的同步方法写法相当与带参的使用action.payload赋值
const {getDataInfo} = counterSlice.actions
const fetchData = () => {
return async (dispatch) => {
if (!counterSlice.getInitialState().table){
return
}
const response = await axios.post(url)
dispatch(getDataInfo(response.data.data))
}
}
export{fetchData }
使用的话跟之前一样 dispatch(函数名)
假设页面加载获取
useEffect(() => {
function init() {
dispatch(fetchData())
}
init()
}, [dispatch])
全部代码
1./src/page/count/index.jsx
import {Button, Card, Pagination} from "antd";
import {increment, decrement, incrementByAmount, decrementByAmount} from '../../store/module/counterSlice'
import {useDispatch, useSelector} from "react-redux";
import {fetchData} from "../../store/module/counterSlice";
import {useEffect} from "react";
import {Table, Tag} from "antd";
import {PlusOutlined, MinusOutlined} from "@ant-design/icons";
import {useNavigate} from "react-router-dom";
import {isAnyOf} from "@reduxjs/toolkit";
const columns = [
//自定义
]
function CountIndex() {
const dispatch = useDispatch()
const navigate = useNavigate();
useEffect(() => {
function init() {
dispatch(fetchData())
}
init()
}, [dispatch])
// 取出存入Redux中的数据信息
const table = useSelector(state => state.counter.table)
const count = useSelector(state => state.counter.count);
// params 1 分页信息 2 自定义列过滤器 3 排序信息 4 数据信息
const handleChange = (pagination, filters, sorter, extra) => {
}
return (
<div>
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
<Button type="primary" ghost icon={<PlusOutlined/>} onClick={() => dispatch(increment())}></Button>
<Button type="primary" ghost style={{marginLeft: 10}}
onClick={() => dispatch(incrementByAmount(10))}>加10</Button>
<h3 style={{color: "green"}}>{count}</h3>
<Button danger ghost icon={<MinusOutlined/>} onClick={() => dispatch(decrement())}></Button>
<Button danger ghost style={{marginLeft: 10}}
onClick={() => dispatch(decrementByAmount(10))}>减10</Button>
</div>
{table &&
<Card bordered={false} style={{backgroundColor: '#808080', width: '1020px', height: '520px'}}>
<Table dataSource={table} rowKey="dataId" columns={columns}
onChange={handleChange} style={{height: '400px'}} size='small'>
</Table>
</Card>
}
</div>
)
}
export default CountIndex
2. /src/store/moudle/counterSlice.jsx
import {createSlice} from "@reduxjs/toolkit";
import axios from "axios";
const counterSlice = createSlice({
name: "counter",
initialState: {
count: 0, //计数字段
table: [] //表格数据
},
reducers: {
increment(state) {
state.count += 1;
},
decrement(state) {
if (state.count <= 0) {
state.count = 0;
return
}
state.count -= 1;
},
incrementByAmount(state, action) {
state.count += action.payload;
},
decrementByAmount(state, action) {
state.count -= action.payload
if (state.count <= 0) {
state.count = 0;
}
},
getDataInfo(state, action) {
state.table = action.payload;
},
setDataInfo(state, action) {
state.table = action.payload;
}
},
})
// 异步获取数据信息
const {getDataInfo} = counterSlice.actions
const fetchData = () => {
return async (dispatch) => {
if (!counterSlice.getInitialState().table){
return
}
const response = await axios.post(url)
dispatch(getDataInfo(response.data.data))
}
}
// 分别导出 同步 异步 默认导出小仓库
export const {increment, decrement, incrementByAmount, decrementByAmount,setDataInfo} = counterSlice.actions;
export {fetchData}
export default counterSlice.reducer;