一、redux
1. 导包
npm i redux
// 或
yarn add redux
2. 创建store(仓库)和reducer(仓库管理员)
src/store/reducer.js
const defaultState = {
num: 0 // 初始化一个数据
}
// 导出一个函数(仓库,动作)
export default (state = defaultState, action) => {
return state
}
src/store/index.js
import { legacy_createStore as createStore } from "redux";
import redurce from "./redurce";
export default createStore(redurce)
3. 组件中使用redux
src/store/reducer.js
在reducer中定义一个事件,让组件来触发,以修改store
const defaultState = {
num: 0
}
export default (state = defaultState, action) => {
// 将type和data从action对象中解构出来,方便后面使用
const {type, data} = action;
// 如果action的type为add,这执行此代码,返回新的store数据
if(type === 'add') {
// store的num累加传过来的数值
state.num += data;
// 需要深拷贝,返回新的state数据
let newState = JSON.parse(JSON.stringify(state))
return newState;
}
// 默认返回原state对象
return state
}
src/App.jsx
import React, { useEffect, useState } from 'react'
// 导入 store 仓库
import store from '../store'
export default function App() {
return (
<div>
// store.getState()获取store的值
<h2>{ store.getState().num }</h2>
// 定义一个add累加方法
<button onClick={() => add(2)}>+</button>
</div>
)
const add = (data) => {
// store.dispatch()来触发reducer中的事件,修改store
// 需要传一个action对象,格式为 {type: name, data: xx}
// action对象的type属性需要和reducer中action对应type一致
store.dispatch({
type: 'add',
data
})
}
// 大坑:redux中,当store数据更新后,界面数据并不会直接更新,需要手动更新
// 初始化一个update数据使用useState(),主要是为了通过setUpdate()来更新组件
// 模拟render()生命周期,实现组件重新加载,以更新界面的store数据
const [update,setUpdate] = useState({})
// useEffect模拟componentDidMount()生命周期
useEffect(() => {
// store.subscribe()是redux提供的,监测store更新的函数
store.subscribe(() => {
// 当store数据更新后执行 setUpdate() ,组件重新加载,实现界面store数据更新
setUpdate({})
})
})
}
**注意:**redux中,当store数据更新后,界面数据并不会直接更新,需要手动更新
如果是函数式组件,手动更新方式如上
类式组件,使用生命周期直接在store.subscribe()中调用setState()即可实现更新数据
componentDidMount() {
store.subscribe(() => {
this.setState({})
})
}
二、react-redux(todo案例)
1. 导入依赖
redux和react-redux
还有antd
yarn add redux react-redux antd
2. code
main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import 'antd/dist/antd.css'
// 导入Provider从react-redux中
import {Provider} from "react-redux"
// 导入store
import store from "./store";
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
// 使用Provider组件包嵌套需要使用store的组件
// 并且将store传进来
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
)
src/store/constant.ts
用来存储action对象的type属性
export const AddTodo = 'ADD_TODO'
src/store/index.ts
import { legacy_createStore as createStore } from "redux";
import reducer from "./reducer";
export default createStore(reducer)
src/store/reducer.ts
import {AddTodo} from "./constant";
const defaultState = {
// 初始化数据
todo: [
{ id: '001', name: 'code' },
{ id: '002', name: 'sleep' }
]
}
export default (state = defaultState, action: {type: string, data?:any}) => {
const {type, data} = action
switch (type) {
case AddTodo:
state.todo.unshift(data)
return JSON.parse(JSON.stringify(state))
default:
return state
}
return state
}
src/component/Todo/index.tsx
import { Button, Input, List, message } from 'antd'
import './index.less'
import {connect} from "react-redux";
import {useState} from "react";
import {AddTodo} from "../../store/constant";
import { nanoid } from 'nanoid'
function index(props: any) {
const data = props.todo
const [inputTodo, setInputTodo] = useState('');
const addTodo = () => {
if(inputTodo.trim() === '') {
return message.info('请输入人内容');
}
props.addTodo({
id: nanoid(),
name: inputTodo
})
}
return (
<div className='todo_container'>
<div className="todo">
<div className="top">
<Input placeholder="Basic usage" value={inputTodo} onChange={(e) => setInputTodo(e.target.value)} />
<Button onClick={addTodo} style={{marginLeft: '10px'}} type="primary">提交</Button>
</div>
<List
size="large"
bordered
dataSource={data}
renderItem={item => {
// @ts-ignore
return <List.Item>{item.name}</List.Item>
}}
/>
</div>
</div>
)
}
// 状态映射
const mapStateToProps = (state: any) => {
return {
todo: state.todo
}
}
// 事件派发映射
const mapDispatchToProps = (dispatch: {type:string, data: any}) => {
return {
addTodo(data: any) {
// @ts-ignore
dispatch({
type: AddTodo,
data
})
}
}
}
// @ts-ignore
export default connect(mapStateToProps, mapDispatchToProps)(index)