Store的宗旨是为了复杂组件数据共享而设计,因此使用场景在设计层尽可能减少,否则极大加剧了项目的复杂度,正如官方所说的那样在你考虑用和不用的时候,那就是不用。
react的社区是足够大的,他不像vue那样cli官方给你构建一套完整的全家桶,如vue+vue-router+vuex
,它更多的是像买车一样,给你提供选配的模式,出了本身的基础配置之外,你是要定制的皮制座椅(flux
)还是星空顶(redux
)…需要什么样的插件,自己就去市场选配什么插件,我们今天来讲一下主流的redux
View调用store.dispatch发起Action->store接受Action(action传入reducer函数,reducer函数返回一个新的state)->通知store.subscribe订阅的重新渲染函数
在前几年我们使用的就是这样,没错,非常完整规范的redux操作流程。reducer
、action
、store
清晰明了,就是有一点,em…老太婆裹脚—又臭又长。所以在vue2发布以后,我们团队果断选择了vue
直到现在,我个人目前也更为擅长vue的各个版本。
store.js
import {createStore} from 'redux'
import Reducer from './reducer'
const Store = createStore(Reducer)
export default Store;
Reducer.js
const Reducer = (state,action)=>{
if(typeof state === 'undefined'){
return []
}
switch (action.type){
case 'CON_TODO':
return [...state,action.text]
break;
case 'TONN_DEL':
state.splice(action.text,1)
return state
break;
default:
return state
break;
}
}
export default Reducer;
action.js
var Action = (text)=>{
return{
type:"CON_TODO",
text:text
}
}
export default Action;
实际使用
import React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
import Action from './Store/action.js'
import Store from './Store/store.js'
class App extends React.Component{
constructor(props){
super(props)
this.shuju = this.shuju.bind(this)
this.state={
arr:Store.getState()
}
}
del(a){
Store.dispatch(Action1(a))
}
componentDidMount(){
Store.subscribe(this.shuju)
}
}
@reduxjs/toolkit这个是我们react
实战项目中的一个重点知识,能帮我们解决redux多余的代码,而且让我们的代码更加清晰明了。以下是一个简单的案例,我们引入了一个关于counter的reducer切片
// store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import counterSlice from './counterSlice'
export const store = configureStore({
reducer: {
counter: counterSlice
},
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
再来看一下具体的切片内容:
// store/counterSlice.ts
import { createSlice, PayloadAction, createAsyncThunk } from '@reduxjs/toolkit'
export interface CounterState {
isLogin: boolean
value: number
featchVal: number
}
const initialState: CounterState = {
isLogin: false,
value: 0,
featchVal: 0
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload
}
}
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
我们如果想加上store的持久化呢,也就是说对store的内容进行本地存储,这里使用的是redux-persist
,此时注意了,@reduxjs/toolkit
仍然支持,也就是说项目基本不需要引入redux
的任何依赖,它本身就是对redux
的功能的封装与补充,而且支持自定义的中间件middleware
,再来看一下:
// store/index.ts
import { configureStore, combineReducers } from '@reduxjs/toolkit'
import { persistStore,persistReducer} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import counterReducer from './counterSlice'
// ...
export const rootReducer = combineReducers({
counter: counterReducer
})
const persistConfig = {
key: 'root',
storage ,
blacklist:[]
}
const myPersistReducer = persistReducer(persistConfig, rootReducer)
export const store = configureStore({
reducer: myPersistReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
})
})
export const persistor = persistStore(store)
通常情况下store是没有处理异步的能力的,所以我们更多的是通过市场上提供的一些处理异步的解决方案,如redux-thunk
但是我们这里不需要,因为仍然有处理异步的方案,我们接着在counterSlice.ts
中进行拓展
import { createSlice, PayloadAction, createAsyncThunk } from '@reduxjs/toolkit'
export interface CounterState {
isLogin: boolean
value: number
featchVal: number
}
const initialState: CounterState = {
isLogin: false,
value: 0,
featchVal: 0
}
export const fetchSettimeout = createAsyncThunk(
'counter/fetchSettimeout', (id: number)=>{
return new Promise<number>((resolve) => {
setTimeout(()=>{
resolve(id)
},3000)
})
}
)
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload
}
},
extraReducers: (builder) => {
builder.addCase(fetchSettimeout.fulfilled, (state, action) => {
console.log(state, action)
state.featchVal = action.payload
})
}
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
我们用 fetchSettimeout
方法封装了一个setTimeout
来模拟了异步,这里只是一种写法,更多的查看点击前往官网
项目中我们希望借助react-redux
来实现store的管理,但是不能直接去使用其中的一些api,可能会导致ts错误,因此我们单独拆出来做了一层Hook封装并进行类型声明
// src/hooks
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import { store } from '@/store'
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
root.render(
<React.StrictMode>
<Provider store={ store }>
<App />
</Provider>
</React.StrictMode>
)
import { RootState,useAppDispatch,useAppSelector } from '@/hooks/store'
import { increment, decrement, fetchSettimeout } from '@/store/counterSlice'
const Home: React.FC = () => {
const dispatch = useAppDispatch()
const {featchVal, value} = useAppSelector((state: RootState) => state.counter)
return (
<div className="home">
数据变化:{featchVal}
数据:{value}
<Button type="primary" onClick={() => dispatch(increment())}>store点击++</Button>
<Button type="primary" onClick={() => dispatch(fetchSettimeout(21313213))}>点击请求</Button>
</div>
}
export default Home
不知道大家看出来什么没有,有没有跟vuex
的语法特别接近了呢,正所谓所谓合久必分,分久必合。任何一个框架除了性能作为首选推荐之外,其次就要考虑哪种语法更贴合开发的使用习惯,更简单明了。不知道当前的你是不是还在使用最原始的redux+local
来管理数据呢???
vuex | @reduxjs/toolkit |
---|---|
name | name |
state | initialState |
mutations | createSlice-reducers |
actions | createAsyncThunk+extraReducers |
modules | configureStore-reducer |