不可变性
Redux状态的更新都是不可变的
如果想要不可变的方式来更新,那么代码就必须先复制原来的array/object,然后在进行更新,展开符(...)就可以起到这个作用
const obj1 = {
a:{
name:summer
},
b:2
}
const obj2 = {
...obj1,
b:22
}
const arr1 = [1,2,3];
//创建arr1的备份并将5放在最后
const arr2 = arr1.concat(5)
Action
Action是一个包含了type,payload的对象
type:是指action的类型payload:是指action携带的数据
action需要通过dispatch来发送
一般情况下,action不会直接写为一个对象,而是需要一个函数,它返回的是一个action(Action Creator)
const addTodo = (text)=>{
return {
type:'ADD_TODO',
payload:text
}
}
所以现在如果想触发这个动作,只需要dispatch(addTodo(text))
Reducer
reducer是一个接受state和action的函数
reducer禁止直接修改state,redux要求一切都是不可变的,必须通过复制然后进行修改
因为type可能有很多,所以我们一般使用if/else,switch来判断更新哪个状态
const initialState = {
a: 'a',
b: 'b'
};
function someApp(state = initialState, action) {
switch (action.type) {
case 'CHANGE_A':
return { ...state, a: 'Modified a' };
case 'CHANGE_B':
return { ...state, b: action.payload };
default:
return state
}
}
reducer里面不可以进行
- 直接操作state
- 请求
api - 调用不纯的函数:
Math.random(),Data.now()
什么是纯函数
- 给相同的参数,返回的一直是相同的输出
- 没有任何副作用(发送网络请求等)’
一般我们会将Reducer拆开,这时候合并的时候我们就需要combineReducers
import {combineReducers} from 'redux'
const rootReducer = combineReducers({
a:ReducerA,
b:ReducerB
})
Store
store就是用来连接action和reducer都东西
作用:
-
提供
getState()方法来获取State -
提供
dispatch方法来发送action更改Statedispatch(addTodo(text)) -
提供
subscribe函数,注册回调函数监听state的更改
创建一个Store很容易,只需要将刚刚的RootReduer传递过去
import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({
reducer:rootReducer
})
dispatch
他是更改state的唯一方法:调用store.dispatch()并传入一个action对象
//action
const increment = () => {
return {
type: 'counter/increment'
}
}
store.dispatch(increment())
RTK新增
切片
"切片"是应用中单个功能的Redux reducer逻辑和action的集合
import { configureStore } from '@reduxjs/toolkit'
import usersReducer from '../features/users/usersSlice'
import postsReducer from '../features/posts/postsSlice'
import commentsReducer from '../features/comments/commentsSlice'
export default configureStore({
reducer: {
users: usersReducer,
posts: postsReducer,
comments: commentsReducer
}
})
configureStore
这个取代了之前的createStore
之前使用createStore时
import { createStore } from 'redux';
import rootReducer from './reducers';
let store = createStore(rootReducer);
现在可以是
import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './reducers'
// 这个store已经集成了redux-thunk和Redux DevTools
const store = configureStore({ reducer: rootReducer })
createAction和CreateReducer
之前Action
const INCREMENT = 'counter/increment'
function increment(amount:number){
return {
type:INCREMENT,
payload:amount
}
}
const action = increment(3)
const initialState = {
a: 'a',
b: 'b'
};
function someApp(state = initialState, action) {
switch (action.type) {
case 'increment':
return { ...state, a: 'Modified a' };
default:
return state
}
使用createAction和createReducer
内部集成了immer,它可以保证不变性
并且不再需要这种switch
import { createAction, createReducer } from '@reduxjs/toolkit'
//创建接口
interface CounterState{
value:number
}
//创建Action
//就省去了Action Create这一步,因为内部已经做了
const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')
const incrementByAmount = createAction<number>('counter/incrementByAmount')
//reducer需要两参数,一个是state,一个是action
const initState:countState = {value:0}
//创建Reducer
const countReducer = createReducer(initState,(builder)=>{
builder
.addCase(increment,(state,action)=>{
state.value++
})
.addCase(decrement,(state,action)=>{
state.value--
})
.addCase(incrementByAmount,(state,action)=>{
state.value+=action.payload
})
})
build提供了3中方法
addCase:根据Action提供一个Reducer case操作addMatcher:可以进行过滤addDefaultCase:默认值,等价于switch
CreateSlice
createSlice相当于将Action和Reducer结合起来
接收initial state、reducer、action creator和action types
您所要做的就是为这个切片定义一个名称,编写一个包含 reducer 函数的对象,它会自动生成相应的 action 代码
//设置iniState类型
export interface InitStateProps{
loading:boolean;
visible:boolean;
isEditMode:ooolean;
fromValue:CustomerTypes;
customerList:CustomerTypes[];
fetchParams:ParamsTypes;
}
//设置initState
const initState:InitStateProps{
loading:false;
visible:false;
isEditMode:false;
fromValue:{};
customerList:[];
fetchParams:{};
}
//创建一个slice
const customerSlice = createSlice({
name:customer,//命名空间
initState,
reducers:{
changeLoading: (state: InitialStateTypes, action: PayloadAction<boolean>) => {
state.loading = action.payload;
},
changeCustomerModel: (state: InitialStateTypes, action: PayloadAction<IndexProps>) => {
const { isOpen, value } = action.payload;
state.visible = isOpen;
if (value) {
state.isEditMode = true;
state.formValue = value;
} else {
state.isEditMode = false;
}
}
}
})
在React中使用
import React,{useState} from 'react'
import {useSelector,useDispatch} from 'react-redux'
import {
changeCustomerModel,
changeLoading
} from '@root/store/reducer/customer';
export default()=>{
const dispatch = useDispatch();
//取值
const {loading,visible,isEditMode,formValue,customerList,fetchParams} = useSelector(
//state.name
(state:ReducerTypes)=> state.customer
)
return (
<div>....</div>
)
}
reducer里面的action,其实就是你当时调用的时候传进去的参数
Prepare和Reducer
一般情况下,我们直接使用createSlice,里面加一个含有reducer的函数,就可以了
但是我们有时候需要从不同的组件dispatch不同的组件,这时候我们就需要用到prepare
const postSlice = createSlice({
name: 'posts',
initialState: initState,
reducers: {
// postAdd(state, action) {
// console.log("state",state);
// console.log("action",action);
// state.push(action.payload)
// },
postAdd: {
reducer(state, action) {
console.log("reducer-action",action);
console.log("reducer-state",state);
state.push(action.payload)
},
prepare({ title, content, userId }) {
console.log("prepare",title, content, userId);
return {
payload: {
id: nanoid(),
title,
content,
user: userId
}
}
}
},
postUpdate(state, action) {
const { id, title, content } = action.payload
const existingPost = state.find(post => post.id === id)
existingPost.title = title
existingPost.content = content
}
}
})
prepare会接受更多的参数,里面也可以写不纯的函数,他会返回一个payload对象,最后createslice会将return出来的payLoad作为新的action,再到reducer·里面使用
useSelector
可以从Redux的Store状态中提取他需要的任何数据
.....
const {loading,visible,isEditMode,formValue,customerList,fetchParams} = useSelector(
//state.name
(state:ReducerTypes)=> state.customer
)
获取state.name,就是当时你createSlice设置的name
参考链接: