Redux
概述
Redux(作者:Dan Abramov,同时也是React核心团队成员)是 JavaScript 状态容器,提供具有响应式特性的全局状态管理
作用类似于Vue.js中的Vuex,可以在组件间共享全局状态数据
Redux可以运行于Node.js服务器端应用、Web客户端应用、原生App应用;也可以配合jQuery、React、Angular等框架组合使用。
面试题:Redux和H5中的sessionStorage / localStorage的区别?
① Redux是保存在内存中的,刷新页面即恢复初始值,无法持久化保存;sessionStorage保存在内存中,localStorage保存在磁盘中,刷新也不会丢失
② Redux中的数据具有响应式特征,订阅了该数据的组件都会自动发生改变;sessionStorage / localStorage中的数据不具备响应式特征
③ Redux可以用于Web客户端应用、原生App应用、Node.js服务器端应用;sessionStorage / localStorage主要应用于Web客户端页面间的数据共享
四大核心概念
① state
应用在当前时刻需要记录的需要具备响应式特性的数据,尤其是需要在多个组件间共享的数据,故需要集中存储
例如:服务器响应数据、缓存数据、本地生成尚未持久化到服务器的数据、UI 状态数据等
React组件也有“状态(state)”数据,二者有本质的不同
① Redux中的state是没有setState()方法的,即不能随意修改
② Redux中的state用于多组件共享,而React中的state仅在当前组件中使用
③ React中的state数据具有“合并”特性;而Redux中的数据修改不具备“合并”特性
② action
要对数据执行的动作;要想更新 state 中的数据,需要发起一个 action
每个action都是一个普通 JavaScript 对象,用来描述发生想要对state执行什么操作
每个action都需要有一个type属性,描述操作的目标
{ type: 'CHANGE_NEWS_PNO', pno: 6 }
{ type: 'TOGGLE_AGREEMENT', value: true }
{ type: 'SET_FAVORITE_TOPICS', list: [10, 25, 31] }
③ reducer
指定对 state 执行 action 动作应该如何实现
reducer是一些纯函数,接收当前的state和要执行的action作为参数,返回根据action修改后的新的state对象,从而实现action想要实现的操作
function appReducer(state = initState, action){
if(action.type === ' CHANGE_NEWS_PNO '){
return { ...state, newsPageNum: action.pno }
}else if(action.type === ' TOGGLE_AGREEMENT '){
return { ...state, agreementChecked: action.value }
}else if(action.type === ' SET_FAVORITE_TOPICS '){
return { ...state, favoriteTopics: action.list }
}else {
return state
}
}
④ store
状态数据存储仓库; 所有的状态最终都被存储到一个唯一的“存储仓库”中
仓库的职责
① 存储所有的state
② 提供 getState( ) 供外界查询数据状态 —— 返回全部的状态数据
③ 提供 dispatch(action) 供外界更新状态
④ 通过 subscribe(listener) 注册监听器
⑤ 通过 subscribe(listener) 返回的函数注销监听器
演示1:在Node.js应用中使用Redux
const { createStore } = require("redux");
// 概念1state
let inisState = {
registeredUser: 0, //注册用户数
onlineUser: 0, //在线用户数
};
//概念2action
// {type:"LOGIN"}
// {type:"LOGOUT"}
// {type:"ONE_REGISTER"}
// {type:"BATCH_REGISTER",count:50}
//概念3reducer
function appReducer(state = inisState, action) {
console.log("state update");
if (action.type === "LOGIN") {
return { ...state, onlineUser: state.onlineUser + 1 };
} else if (action.type === "LOGOUT") {
return { ...state, onlineUser: state.onlineUser - 1 };
} else if (action.type === "ONE_REGISTER") {
return { ...state, registeredUser: state.registeredUser + 1 };
} else if (action.type === "BATCH_REGISTER") {
return { ...state, registeredUser: state.registeredUser + action.count };
} else {
return state;
}
}
//概念4store
let store = createStore(appReducer);
// 查看仓库中目前的状态
// console.log(store.getState());
// 订阅仓库的状态更新
store.subscribe(() => {
console.log("收到订阅消息,仓库状态改变");
console.log(store.getState());
});
// 使用定时器修改仓库的状态
setInterval(() => {
store.dispatch({ type: "ONE_REGISTER" });
}, 1000);
setInterval(() => {
store.dispatch({type:"BATCH_REGISTER",count:10});
}, 3000);
演示2:在React应用中使用Redux(单一文件)
@app.js
import React, { useState } from "react";
import { createStore } from "redux";
// state
let initState = {
buyCount: 1,
};
// action
// {type:"ENCREASE"}
// {type:"DECREASE"}
// reducer
function appReducer(state = initState, action) {
console.log("state updated");
if (action.type === "ENCREASE") {
return { ...state, buyCount: state.buyCount + 1 };
} else if (action.type === "DECREASE") {
let buyCount = state.buyCount - 1;
buyCount = buyCount < 0 ? 0 : buyCount;
return { ...state, buyCount };
} else {
return state;
}
}
//store
let store = createStore(appReducer);
function CaryShow() {
let [count, setCount] = useState(store.getState().buyCount);
// 订阅仓库状态的修改
store.subscribe(() => {
setCount(store.getState().buyCount);
});
return <div>当前购物车中商品数量:{count}</div>;
}
function CartModify() {
return (
<>
<button onClick={() => store.dispatch({ type: "DECREASE" })}>
-
</button>
<button onClick={() => store.dispatch({ type: "ENCREASE" })}>
+
</button>
</>
);
}
export default function App() {
return (
<div>
<CaryShow></CaryShow>
<hr></hr>
<CartModify></CartModify>
</div>
);
}
演示3:在React应用中使用Redux(多个文件)
@context.js
import { createStore } from "redux";
import React from "react";
// state
let initState = {
buyCount: 5,
};
// action
// {type:"ENCREASE"}
// {type:"DECREASE"}
// reducer
function appReducer(state = initState, action) {
console.log("state updated");
if (action.type === "ENCREASE") {
return { ...state, buyCount: state.buyCount + 1 };
} else if (action.type === "DECREASE") {
let buyCount = state.buyCount - 1;
buyCount = buyCount < 0 ? 0 : buyCount;
return { ...state, buyCount };
} else {
return state;
}
}
// store
export let store = createStore(appReducer);
//上下文对象=为所有自足案件提供共享数据:store
let StoreContext = React.createContext(store);
export default StoreContext;
@index.js
import React from "react";
import ReactDOM from "react-dom/client";
import CartModify from "./components/CartModify";
import CartShow from "./components/CartShow";
import StoreContext, { store } from "./context";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<StoreContext.Provider value={store}>
<CartShow></CartShow>
<hr />
<CartModify></CartModify>
</StoreContext.Provider>
);
@components\CartModify.js
import React, { useContext } from "react";
import StoreContext from "../context";
import mapDispatch from "./mapDispatch";
// export default function CartModify() {
// let ctx = useContext(StoreContext);
// return (
// <div>
// <button onClick={() => ctx.dispatch({ type: "DECREASE" })}>
// -
// </button>
// <button onClick={() => ctx.dispatch({ type: "ENCREASE" })}>
// +
// </button>
// </div>
// );
// }
function CartModify(props) {
return (
<div>
<button onClick={() => props.dec()}>-</button>
<button onClick={() => props.enc()}>+</button>
</div>
);
}
export default mapDispatch(CartModify);
@components\CartShow.js
import React, { useContext, useState } from "react";
import StoreContext from "../context";
import mapStateToProps from "./mapStateToProps";
// export default function CartShow() {
// let ctx = useContext(StoreContext);
// let [count, setCount] = useState(ctx.getState().buyCount);
// ctx.subscribe(() => {
// setCount(ctx.getState().buyCount);
// });
// return <div>当前购物车数量:{count}</div>;
// }
function CartShow(props) {
return <div>当前购物车数量:{props.bc}</div>;
}
export default mapStateToProps(CartShow);
@components\mapDispatch.js
import { useContext } from "react";
import StoreContext from "../context";
// 将store的dispatch(action)方法映射为指定组件的props
export default function mapDispatch(UserComponent) {
function NewComponent(props) {
let ctx = useContext(StoreContext);
let enc = () => {
ctx.dispatch({ type: "ENCREASE" });
};
let dec = () => {
ctx.dispatch({ type: "DECREASE" });
};
return <UserComponent {...props} enc={enc} dec={dec}></UserComponent>;
}
return NewComponent;
}
@components\mapStateToProp.js
import { useContext, useState } from "react";
import StoreContext from "../context";
// 将store中的状态数据,映射为制定组件的props
export default function mapStateToProps(UserComponent) {
function NewComponent(props) {
let ctx = useContext(StoreContext);
let [count, setCount] = useState(ctx.getState().buyCount);
ctx.subscribe(() => {
setCount(ctx.getState().buyCount);
});
return <UserComponent {...props} bc={count}></UserComponent>;
}
return NewComponent;
}