1.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客户端页面间的数据共享
四大核心概念
安装Redux
npm i redux
-
① 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')
// 概念1:state
let initState = {
registerUser: 0, //注册用户数量
onLineUser: 0 //在线用户的数量
}
// 概念2: action
// 概念3:reducer
function appReducer(state = initState, action) {
console.log('---修改旧状态--');
if (action.type === 'LOGIN') {
return { ...state, onLineUser: state.onLineUser + 1 }
} else if (action === 'LOGOUT') {
return { ...state, onLineUser: state.onLineUser - 1 }
} else if (action.type === 'ONE_REGISTER') {
return { ...state, registerUser: state.registerUser + 1 }
} else if (action.type === 'BATCH_REGSITER') {
return { ...state, registerUser: state.registerUser + action.count }
} else {
return state
}
}
//概念4 store
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_REGSITER', count: 10 })
}, 3000);
示例2:在React应用中使用Redux(单一文件)
import React, { useState } from 'react'
import { createStore } from 'redux';
// 状态1;state
let initState = {
buyCount: 2
}
// 概念2 action
// 概念3 reducer
function appReducer(state = initState, action) { //1.当前值2.目标
console.log('--state 更新了---');
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: buyCount }
} else {
return state
}
}
// 概念4 stroe
let stroe = createStore(appReducer)
function CartShow() {
let [count, setCount] = useState(stroe.getState().buyCount)
// 订阅仓库状态的修改
stroe.subscribe(() => {
setCount(stroe.getState().buyCount)
})
return <div>当前的商品数量:{count}</div>
}
function CartModify() {
return (
<>
<button onClick={() => { stroe.dispatch({ type: 'DECREASE' }) }}>-</button>
<button onClick={() => { stroe.dispatch({ type: 'ENCREASE' }) }}>+</button>
</>
)
}
export default function App() {
return (
<div>
<CartShow />
<hr />
<CartModify />
</div>
)
}
2.React-Redux
问题
- 在多模块文件的React应用中,如果在根模块中创建 Redux store,然后通过props依次传递给所有的子组件,实在太麻烦了
方案
-
react-redux模块可用于使用了Redux的React项目中,提供了两方面的扩展功能
- ① 一个“Context.Provider”:为它所包含的所有子组件提供store使用
- ② 一个HOC高阶组件函数:connect()函数将自定义组件提升为“高阶组件”,将Redux的dispatch和state映射为被包装组件的props
步骤
- ① 安装依赖模块 npm i redux react-redux
- ② 顶级组件中创建 store 和 reducer,使用Provider为子组件共享store
- ③ 将store.dispatch()映射为子组件的props
- ④ 将store.getState()映射为子组件的props
index.js:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import CartModify from './components/CartModify';
import CartShow from './components/CartShow';
// 1.state
let initState = {
byCount: 1
}
// 2.action
// 3.reducer
function appReducer(state, action) {
switch (action.type) {
case 'ENCREASE':
return { ...state, byCount: state.byCount + 1 }
case 'DECREASE':
return { ...state, byCount: state.byCount - 1 }
default:
return initState
}
}
// 4.store
let store = createStore(appReducer)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<CartShow />
<hr />
<CartModify />
</Provider>
);
CartShow.js:
import React from 'react'
import { connect } from 'react-redux';
function CartShow(props) {
console.log('props1', props);
return (
<div>当前购物车中的购买数量:{props.bc}</div>
)
}
function mapStateToProps(state) {
console.log('state1', state);
return {
bc: state.buyCount
}
}
export default connect(mapStateToProps, null)(CartShow)
CartModify.js:
import React from 'react'
import { connect } from 'react-redux';
function CartModify(props) {
console.log(props);
return (
<div>
<button onClick={props.dec}>-</button>
<button onClick={props.enc}>+</button>
</div>
)
}
function mapDispatchToProps(dispatch) {
return {
enc: () => {
dispatch({ type: 'ENCREASE' })
},
dec: () => {
dispatch({ type: 'DECREASE' })
}
}
}
export default connect(null, mapDispatchToProps)(CartModify)