一、Redux核心概念
1、state
(1)作用:存储数据
(2)注意:Redux不负责驱动视图,只是负责计算数据、保存数据
2、action
(1)本质:具体type字段的JS对象;
(2)作用:描述一个动作;
3. dispatch
(1)作用:触发一个action
(2)特点:redux中修改state唯一的方式就是dispatch(Action)
4. reducer
(1)本质:纯函数
(2)作用:负责计算新的状态(数据)
(3)注意:
①不可变数据,需新值覆盖旧值
②不能写请求、操作DOM
二、Redux在项目中使用(js版)
1.安装redux
npm i redux
2.React-Redux插件
作用:简化Redux在React中的使用
npm i react-redux
3.创建store仓库文件结构,大致如下:
src
├─store
└─reducer
│ count.js
│ index.js // 入口文件合并多个 reducer
├─action
│ actionTypes.js // actionType 提示文件
│ count.js
└─index.js // store 入口文件
以一个count计算为例子:
4.在src/store/reducer/count.js创建一个reducer,在src/store/reducer/index.js合并所有reducer
const initState = { count: 100 }
export default function countReducer(state = initState, action) {
switch (action.type) {
case 'xxx':
default:
return state
}
}
import { combineReducers } from 'redux';
import countReducer from './count';
import userReducer from './user';
const rootReducer = combineReducers({
count: countReducer,
user: userReducer,
});
export default rootReducer;
5.在src/store/index.js创建store:
import { legacy_createStore as createStore } from 'redux';
import rootReducer from './reducer';
const store = createStore(rootReducer);
export default store;
6.把仓库与项目关联起来(使用插件实现数据驱动视图)
在src/index.js中,加上Provider并关联store:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
7.在页面上获取数据:
使用useSelector获取store中的数据:
import { useSelector } from 'react-redux'
export default function App(){
const count = useSelector(state => state.count)
console.log(count)
}
8.修改仓库的数据(触发dispatch是修改数据的唯一方式):
(1)在src/store/count.js封装一个Action函数:
export const updateCountAction = () => {
return {
type: 'count/add',
payload: null,
};
};
(2)使用dispatch去触发:
import { updateCountAction } from './store/action/count.js'
export default function App(){
const count = useSelector(state => state.count)
//这是插件的dispatch
const dispatch = useDispatch()
return(
<h1>{count}</h1>
<button onClick={ ()=> dispatch(updateCountAction()) }>点击加1</button>
)
}
(3)在reducer中计算数据:
import { legacy_createStore as createStore } from 'redux'
const initState = { count: 100 }
export default function countReducer(state = initState, action) {
switch (action.type) {
+ case 'count/add':
+ return {
+ ...state,
+ count: state.count + 1
}
default:
return state
}
}
(4)优化:使用一个常量去代替type的字符串
1.在src/store/action/actionType.js中定义各个action中type的名字:
export const COUNT_ADD = 'count/add'
2.在action和reducer中分别导入使用:
action:
import { COUNT_ADD } from 'src/action/actionType.js'
export const updateCountAction = () => {
return {
type: COUNT_ADD,
payload: null,
};
};
reducer:
import { legacy_createStore as createStore } from 'redux'
+import { COUNT_ADD } from 'src/action/actionType.js'
const initState = { count: 100 }
export default function countReducer(state = initState, action) {
switch (action.type) {
+ case COUNT_ADD:
return {
...state,
count: state.count + 1
}
default:
return state
}
}
9. 使用中间件redux-thunk发送异步请求
1.安装redux-thunk
npm i redux-thunk
2.导入到store/index.js, 并配置
import { applyMiddleware, legacy_createStore as createStore } from 'redux'
+ import thunk from 'redux-thunk'
// 将 thunk 添加到中间件列表中
- const store = createStore(rootReducer, applyMiddleware())
+ const store = createStore(rootReducer, applyMiddleware(thunk))
export default store
// 这里是安装调试工具redux-devtools-extension
// 1.安装 npm i redux-devtools-extension
// 2.修改 store
// const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))
3.使用
import { COUNT_ADD } from 'src/action/actionType.js'
export const updateCountAction = () => {
// 返回一个异步函数
+ return async (dispatch) => {
+ const res = await loadNewAPI();
// 使用函数形参的dispatch,再次发起dispatch(action)
+ dispatch({ type: COUNT_ADD, payload: res });
+ };
};
三、Redux在项目中使用(ts版)
创建仓库的过程(1-6步)与js一样
不同点:
1.获取仓库的数据使用:useAppSelector()
(1)在创建仓库的时候加上这两句代码(插件官方的)
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { legacy_createStore as createStore } from 'redux';
import rootReducer from './reducer';
const store = createStore(rootReducer);
export default store;
// 获取仓库的数据
+export type RootState = ReturnType<typeof store.getState>;
+export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
(2)获取仓库的数据使用useAppSelector()
const { count } = useAppSelector((state) => state.count);
2.修改数据dispatch使用:useAppDispatch()
(1)在创建仓库的时候加上最后两句代码(插件官方的)
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { legacy_createStore as createStore } from 'redux';
import rootReducer from './reducer';
const store = createStore(rootReducer);
export default store;
// 获取仓库的数据
+export type RootState = ReturnType<typeof store.getState>;
+export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
// 约定action和reducer中的type
+export type AppDispatch = typeof store.dispatch;
+export const useAppDispatch = () => useDispatch<AppDispatch>();
(2)封装action函数
export const updateCountAction = (id: number) => ({
type: 'list/updateCountAction',
payload: id,
});
(3)使用useAppDispatch()去修改数据;
import { useAppDispatch, useAppSelector } from '../store';
import { updateCountAction } from '../store/action';
//使用
const dispatch = useAppDispatch();
//调用
<button onClick={() => { dispatch(updateCountAction()) }}>
(4)reducer计算
import { legacy_createStore as createStore } from 'redux'
const initState = {
count: 100,
list: [
{ id: 1, task: '吃饭', isDone: true },
{ id: 2, task: '睡觉', isDone: true },
{ id: 3, task: '学习', isDone: false },
], }
export default function countReducer(state = initState, action) {
switch (action.type) {
case 'list/updateCountAction':
return {
...state,
count: state.count + 1
}
default:
return state
}
}
(5)类型约定(保证action和reducer中的type一致)
①在每个reducer的ts文件中去定义type的类型,并且绑定给action:
import { legacy_createStore as createStore } from 'redux'
const initState = {
count: 100,
list: [
{ id: 1, task: '吃饭', isDone: true },
{ id: 2, task: '睡觉', isDone: true },
{ id: 3, task: '学习', isDone: false },
], }
//第一个是更新reducer的type, 第二个是清除reducer的type
+export type ActionType = { type: 'list/updateById'; payload: number } | { type: 'list/clearDone' };
// 在ts-react中,不要去解构action
-//export default function countReducer(state = initState, action) {
+export default function countReducer(state = initState, action: ActionType) {
switch (action.type) {
case 'list/updateCountAction':
return {
...state,
count: state.count + 1
}
default:
return state
}
}
②在封装action的函数中加上type
import { ActionType } from '../reducer/list';
export const updateByIdAction = (id: number): ActionType => ({
type: 'list/updateById',
payload: id,
});
export const clearDoneAction = (): ActionType => ({
type: 'list/clearDone',
});
3.Redux指定reducer函数的返回值类型
import { legacy_createStore as createStore } from 'redux'
const initState = {
count: 100,
list: [
{ id: 1, task: '吃饭', isDone: true },
{ id: 2, task: '睡觉', isDone: true },
{ id: 3, task: '学习', isDone: false },
], }
//第一个是更新reducer的type, 第二个是清除reducer的type
+export type ActionType = { type: 'list/updateById'; payload: number } | { type: 'list/clearDone' };
// 在ts-react中,不要去解构action
-//export default function countReducer(state = initState, action) {
-//export default function countReducer(state = initState, action: ActionType) {
+export default function countReducer(state = initState, action: ActionType): typeof initState {
switch (action.type) {
case '':
return {
...state,
count: state.count + 1
}
default:
return state
}
}