本文已参与[新人创作礼]活动,一起开启掘金创作之路
注:本文有部分基础没有写,但主体内容均已列出,如参考请做好心理准备
1.创建单个action库
src\store\reducers\todos.ts
const initState=[
{
id:1,
name:'学习',
isDone:false
},
{
id:2,
name:'玩',
isDone:false
},
{
id:3,
name:'睡觉',
isDone:false
}
]
export default function todos(state=initState,action:any){
return state
}
2.合并action库
src\store\reducers\index.ts
import { combineReducers } from "redux";
import todos from "./todos";
const rootReducer=combineReducers({
todos
})
export default rootReducer
3.配置redux
src\store\index.ts
import { applyMiddleware,legacy_createStore } from "redux";
import rootReducer from './reducers'
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
const store=legacy_createStore(rootReducer,composeWithDevTools(applyMiddleware(thunk)))
export default store
4.挂载到状态树上
src\index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './store';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<Provider store={store}>
<App />
</Provider>
);
5.获取数据
方式1
App
方式2
APP
方式3
typeof
typeof可以获取某个数据的类型
type Fn = typeof fn获取fn函数的类型
ReturnType
ReturnType是一个泛型工具类型,可以获取函数类型的返回值的类型
思路
得到类型
src\store\index.ts
type Fn=typeof store.getState
export type RootState=ReturnType<Fn>
获取类型
src\App.tsx
import { RootState } from './store'
const todos=useSelector((state:RootState)=>state.todos)
6.删除事件
redux
src\store\reducers\todos.ts
export default function todos(state=initState,action:any){
console.log(action);
if(action.type==='DEL_TODO'){
return state.filter(it=>it.id!== action.payload)
}
声明
app
import {useSelector,useDispatch} from 'react-redux'
const dispatch=useDispatch()
const del=(id:number)=>{
console.log(id);
dispatch({type:'DEL_TODO',payload:id})
}
使用
app
<ul>
{
todos.map(item=>(<li key={item.id}>{item.name}
<button onClick={()=>del(item.id)}>删除</button>
</li>))
}
</ul>
优化
app
const del=(id:number)=>{
console.log(id);
dispatch(delTodo(id))
}
src\store\actions\delTodo.ts
export const delTodo = (id:number)=>{
return {
type:'DEL_TODO',
payload:id
}
}
7.优化redux
src\store\actions\index.ts
export type ActionType = {
type:'DEL_TODO',
id:number
}
export const delTodo=(id:number)=>{
return {
type:'DEL_TODO',
id
}
}
src\store\reducers\todos.ts
import { ActionType } from "../actions";
const initState:TodoItem[]=[
{
id:1,
name:'学习',
isDone:false
},
{
id:2,
name:'玩',
isDone:false
},
{
id:3,
name:'睡觉',
isDone:false
}
]
export type TodoItem={
id:number,
name:string,
isDone:boolean
}
export default function todos(state:TodoItem[]=initState,action:ActionType):TodoItem[]{
console.log(action);
if(action.type==='DEL_TODO'){
return state.filter(it=>it.id!== action.id)
}
return state
}
8. 添加事件
app
键盘按下
if(e.keyCode===13){
if(e.key==='Enter'){
if(e.code==='Enter'){
action异步事件注册
src\store\actions\index.ts
export type ActionType = {
type:'DEL_TODO',
id:number
} | {
type:'ADD_TODO',
name:string
}
export const addTodo=(name:string)=>{
console.log(name,'name');
return {
type:'ADD_TODO',
name
}
}
reducer
src\store\reducers\todos.ts
export default function todos(state:TodoItem[]=initState,action:ActionType):TodoItem[]{
console.log(action);
if(action.type==='DEL_TODO'){
return state.filter(it=>it.id!== action.id)
}else if(action.type==='ADD_TODO'){
console.log(action);
return [...state,{name:action.name,id:Date.now(),isDone:false}]
}
return state
}
事件注册
const add=(e:React.KeyboardEvent<HTMLInputElement>)=>{
if(e.key==='Enter'){
console.log(name,'name');
dispatch(addTodo(name))
setName('')
}
事件绑定
方法1
<input onKeyUp={add} value={name} onChange={(e)=>setName(e.target.value) }></input>
方法2
<input onKeyUp={(e)=>add(e)} value={name} onChange={(e)=>setName(e.target.value) }></input>
9.点击样式改变
src\store\actions\index.ts
export const changeStateTodo=(id:number,newIsDone:boolean):ActionType=>{
console.log(id,newIsDone);
return {
type:'CHANG_TODO',
payload:{
id,
newIsDone
}
}
}
reducer
src\store\reducers\todos.ts
else if(action.type==='CHANG_TODO'){
console.log(action);
return state.map(item=>{
if(item.id===action.payload.id){
return {...item,isDone:action.payload.newIsDone}
}else{
return {...item}
}
})
app
事件注册
const changeTodo=(id:number,flag:boolean)=>{
dispatch(changeStateTodo(id,flag))
}
事件绑定
<ul>
<input onKeyUp={(e)=>add(e)} value={name} onChange={(e)=>setName(e.target.value) }></input>
{
todos.map(item=>(<li className={item.isDone?'completed':''} key={item.id}>
<span onClick={()=>changeTodo(item.id,!item.isDone)}>{item.name}</span>
<button onClick={()=>del(item.id)}>删除</button>
</li>))
}
</ul>
10. 初始化数据
设置action异步
src\store\actions\index.ts
import {ActionType} from './actions'
import { ThunkAction } from "redux-thunk"
import axios from "axios"
// thunk类型的变更,使用了thunk之后,返回的Action类型不再是对象,而是函数类型的Action,因此需要修改Action的类型。thunkAction类型的使用
// 参数1:RetrunType 用于指定函数的返回值类型void
// 参数2:指定RootState的类型
// 参数3:指定额外的参数类型,一般为unkonwn或者any
// 参数4:用于指定dispatch的Action类型
export const initTodo=():ThunkAction<void,RootState,unknown,ActionType>=>{
return async (dispatch)=>{
const res=await axios.get('####')
console.log(res);
dispatch({
type:'INT_TODO',
data:res.data.data
})
}
}
触发
app
useEffect(()=>{
dispatch<any>(initTodo())
},[dispatch])
优化
src\store\index.ts
import {ActionType} from './actions'
import { ThunkAction } from "redux-thunk"
export type RootThunkAction=ThunkAction<void,RootState,unknown,ActionType>
src\store\actions\index.ts
import axios from "axios"
import {RootThunkAction} from '../index'
import { TodoItem } from "../reducers/todos"
export type ActionType = {
type:'DEL_TODO',
id:number
} | {
type:'ADD_TODO',
name:string
} | {
type:'INT_TODO',
data:TodoItem[]
}
export const initTodo=():RootThunkAction=>{
return async (dispatch)=>{
const res=await axios.get('####')
console.log(res);
dispatch({
type:'INT_TODO',
data:res.data.data
})
}
}
效果展示
11.注意事项
1.在使用action进行异步操作时,需要使用useDispatch进行相应操作
2.当使用dispatch对action进行异步时,action发axios请求时有的会出现这个问题
此时我们仅需再dispatch后加< any >类型即可
3.注意:出现问题时,如果代码在别人那里没有问题或你认为自己没有问题就需要注意是不是包的版本出了问题
例如:redux-thunk版本不对就会导致即使加了RootThunkAction类型下面type也不会有提示
自己感受一下4年不动,一动就是大版本更新