redux 基础概念
适用场景
- 复杂的用户交互
- 分权限、分不同的角色下
- 多用户数据共享、协作
- 与服务器交互多,使用了websocket
- 页面需要的数据来自多个渠道
基础概念
store
整个应用中只能有一个数据管理的容器对象store
state
包含当前时刻所有的数据.通过store.getState()
得到
action
用于发出通知,更改state
. 通过store.dispatch()
发出Action
reducer
用于处理发出的Action
通知. 需要返回一个全新的state
设计理念
- 单一数据源:易管理、易调试.
state
只读, 保证了数据更改只能通过一种方式修改Action
reducer
纯函数处理数据更新
常用API释义
creatStore
import {createStore,combineReducers} from 'redux'
// reducer
function userReducer(state=INIT_STATE.userInfo,action) {
switch(action.type){
case 'update':
return {
...state,
...action.payload
}
case 'delete':
return action.payload
default:
return state
}
}
// 创建
const store = createStore(reducer)
const {subscribe,dispatch,getState} = store
createStore(reducer,[INIT_STATE],enhancer)
初始化创建redux
容器 . 接受两个参数:reducer
函数 、非必选INIT_STATE
为reducer
函数中参数state
默认值、非必选enhancer
复合高阶函数更改store
接口.reducer(state,action)
处理Action
,参数:当前的state
、要处理的action
combindReducers
合并多个reducer , 参数为一个对象
import {createStore,combineReducers} from 'redux'
// 多个模块 的reducer
import * as UserReducer from './reducers/User'
import * as LogReducer from './reducers/Log'
// 合并reducer 为一个整体
const reducer = combineReducers({UserReducer,LogReducer})
// 创建
const store = createStore(reducer)
数据容器对象 Store
-
getState()
返回当前的 state -
dispacth(action)
分发 acion . 参数接受一个普通对象, 必须包含type
字段表示类型 . -
subscribe(listener)
添加监听器 . state 发生变化时 , 调用 订阅函数listener -
replaceReducer(nextReducer)
替换当前用来计算state
的redcuer
.
中间件
applyMiddleWare(...middleWares)
通过中间键扩展redux .- 每个中间件接受两个参数:
dispatch
和getState
. 返回一个函数next(action)
用于手动调用下一个调用链(用于更改默认发起的aciton,不改变时默认自动调用) . 最后一个调用链会接收到实际真是的dispatch
.
bindActionCreators(actionCreators,dispatch)
格式化将类如 store.dispatch(updateUser())
包装后 可在组件中直接调用 updateUser()
调用的方式.
- 第一个参数为
actionCreator
或者值为actionCreator 的对象 . - 第二个参数为
store
提供的 分发函数.
componse(...fun)
从右到左来组合多个函数.按顺序执行
redux 基础使用
安装
npm install redux --save
基础示例
分步使用:
- 创建
reducer
, - 创建
store
. - 订阅函数.
- 获取当前最新的
store
- 应用于视图UI.
- 数据变化更新视图.
- 获取当前最新的
- 事件分发.
import {createStore} from 'redux'
// 初始state
const INIT_STATE = {
userInfo:{
name:'admin',
age:20,
}
}
// reducer
function userReducer(state=INIT_STATE.userInfo,action) {
switch(action.type){
case 'update':
return {
...state,
...action.payload
}
case 'delete':
return action.payload
default:
return state
}
}
// init
const store = createStore(userReducer)
// 订阅变化
// 变化时,打印数据
store.subscribe(()=>console.log(store.getState()))
// 分发action
store.dispatch({type:'update',payload:{name:'test'}})
store.dispatch({type:'update',payload:{name:'test',age:32}})
store.dispatch({type:'delete'})
store.dispatch({type:'add'})
管理多个 reducer
根据业务量、复杂度 , 仅有一个reducer
处理函数,会变得特别庞大 、难以维护 .
通过拆分模块、划分功能.创建多个reducer
进行单独模块的数据状态管理 .
userInfo.js
用于用户数据的管理,数据更新;
// userInfo.js
const INIT_STATE = {
name:'admin',
age:23,
address:'江苏省 南京市'
}
export function UserInfo(state=INIT_STATE,action) {
const {type,data} = action
switch(type){
case 'updateName':
return {
...state,
name:data,
}
case 'updateAge':
return {
...state,
age:data,
}
default:
return state
}
}
authInfo.js
用于当前登录用户的权限信息,权限更新;
// authInfo.js
const INIT_STATE = {
role:'管理员',
enable:false,
operations:[],
}
export function AuthInfo(state=INIT_STATE,action) {
const {type,data} = action
switch(type){
case 'updateRole':
return {
...state,
role:data,
}
case 'addOperate':
// 拷贝 、 必要时深拷贝
const obj = Object.assign(state)
obj.operations.push(data)
return obj
default:
return state
}
}
reducers.js
redux初始化主入口文件,汇总模块中的reducer
;
// reducers.js
import {combineReducers} from 'redux'
// 导入模块reducer
import {UserInfo as user} from './UserInfo'
import {AuthInfo as auth} from './AuthInfo'
const reducers = combineReducers({
user,
auth,
})
export default reducers
// index.js
import {createStore} from 'redux'
import reducer from './reducers'
// init
const store = createStore(reducer)
// 订阅变化
// 变化时,打印数据
store.subscribe(()=>console.log(store.getState()))
store.dispatch({type:'INIT_STATE'})
store.dispatch({type:'updateName',data:'test'})
store.dispatch({type:'updateRole',data:'测试成员'})
在React
中使用
在不使用react-redux
下, 访问store
;
index.js
项目主入口文件;
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// store
import store from './store'
const render = ()=>ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// store 变化重新执行渲染
render()
store.subscribe(render)
reduxBox/index.js
模拟一个功能模块,获取全局的store
数据。
// reduxBox/index.js
import { lazy } from "react";
import { useRouteMatch, Route, Redirect } from "react-router-dom";
// 直接导入store 数据
import store from '../store/index'
// 组件加载
const UserInfo = lazy(()=>import("./userInfo"))
export default function App(props) {
const { url } = useRouteMatch();
const {user,auth} = store.getState();
return (
<>
<div>
<p>store 数据</p>
<p>姓名:{user.name} - 年龄:{user.age} - 地址:{user.address}</p>
<p>角色名称:{auth.role} - 是否有效:{auth.enable} - 操作权限:{auth.operations}</p>
</div>
<Route exact path={url}>
<Redirect to={`${url}/userInfo`} />
</Route>
<Route path={`${url}/userInfo`} component={UserInfo} />
</>
);
}
reduxBox/userInfo.js
用户信息模块,展示用户信息;可更新用户信息
// reduxBox/userInfo.js
import {Input} from 'antd'
import { useState } from 'react'
// 直接导入store 数据
import store from '../store/index'
export default function UserInfo(props) {
const [name,setName] = useState('')
// 获取数据
const {user} = store.getState();
return (<div style={{border:'1px solid #fff'}}>
<p>当前状态</p>
<p>姓名:{user.name} - 年龄:{user.age} - 地址:{user.address}</p>
<Input onChange={e=>setName(e.target.value)} onPressEnter={e=>store.dispatch({type:'updateName',data:name})} />
</div>)
}
通过Input
输入确定后可以看到组件本身、父组件的数据视图都已更新.似乎这样没有任何的问题;
它能正常工作的原因主要在于,store
数据变化,整个项目重新渲染,拿到最新的数据。
// 初始化渲染
render()
// store 订阅更新, 数据发生变化时调用
// ReactDomo.render() 重复调用时, 进行更新操作,对有必要更改的DOM映射最新为最新的元素.
store.subscribe(render)
这种很明显的缺陷就是完全没有将store
数据融入到react
中, 并不是react
一部分 , 在我们实际业务中,是什么引起的组件更新造成了很大的困扰.
- 不能使用
shouldComponentUpdate
去控制 数据引起的组件更新. - 不能使用
React.PureComponent
创建组件, 因为组件自身永远得不到要更新的理由.
解决的方式就是将store
数据流向需要的组件, 通过 props
或者context
, 由于props
局限(只能父传子), 所以react-redux
使用 context
来实现redux
和react
的数据连通, 将数据映射到组件的props
可以通过context
来让store
数据变化引起视图变化更像react 的风格
// store
import store from './store'
// 创建 context
export const StoreContext = React.createContext({})
const render = ()=>ReactDOM.render(
<React.StrictMode>
<StoreContext.Provider value={store.getState()}>
<App />
</StoreContext.Provider>
</React.StrictMode>,
document.getElementById('root')
);
// store 变化重新执行渲染
render()
// store.subscribe(render)
这是就不需要store.subscribe(render)
去订阅更新了。
但是使用context
还是不能让我们很好的控制组件的数据,虽然它有点像react
风格了 , 所以有了react-redux
react-redux
使用
安装
npm install --save react-redux
引起组件更新的数据更改:state
、props
、context
API 释义
Provider
将redux store
附加在整个应用层 , 类组件通过connect
获取数据和转发action, 函数组件通过 hooks useSelector\useDispatch
import {Provider} from 'react-redux'
// store
import store from './store'
// 创建 context
// export const StoreContext = React.createContext({})
const render = ()=>ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
render()
在函数组件中使用hooks
useSelector
获取store
里的数据.
useDispatch
事件分发函数 .
代码示例说明:
// react-redux
import {useSelector,useDispatch} from 'react-redux'
export default function UserInfo(props) {
const [name,setName] = useState('')
// 获取数据
const {user} = useSelector(state=>{
const {user} = state
return {
user
}
})
// 转发事件
const dispatch = useDispatch()
return (<div style={{border:'1px solid #fff'}}>
<p>当前状态</p>
<p>姓名:{user.name} - 年龄:{user.age} - 地址:{user.address}</p>
<Input onChange={e=>setName(e.target.value)} onPressEnter={e=>dispatch({type:'updateName',data:name})} />
</div>)
}
在class
组件中使用
connect(mapStateToProps,mapDispatchToProps,mergeProps,options)
链接react组件与reduxstore
* `mapStateToProps(gloablState,ownProps)` 链接`store` , 监听redux store变化,只要发生更改,就会更新此组件
* `mapDispatchToProps(dispatch,ownProps)` 定义`action creator` , 通过调用,发起action
* `mergeProps(stateProps,dispatchProps,ownProps)` 默认情况下返回`Object.assign({},wonProps,stateProps,dispatchProps)`
* `options={pure:boolean,withRef:boolean}` `pure` 浅比较mergeProps的结果,避免不必要的更新;
bindActionCreators(fn|object:{fn})
转换为 action creator
,减少样板的重复使用;
// 示例中
dispatch({type:'addOperate',data:values})
// 转换
const updateOperates = bindActionCreators((values)=>{
return {
type:'addOperate',
data:values,
}
},dispatch)
// 然后调用
updateOperates()
示例代码:
/**
* 角色信息
*/
import React from 'react'
import {Select} from 'antd'
import {connect} from 'react-redux'
const {Option} = Select
class RoleInfo extends React.Component{
render(){
// 通过Props获取数据
const {auth,dispatch} = this.props
return(<div style={{border:'1px solid #fff'}}>
<p>当前状态</p>
<p>角色名称:{auth.role} - 是否有效:{auth.enable} - 操作权限:{auth.operations}</p>
<Select style={{width:100}} mode="multiple" onChange={values=>dispatch({type:'addOperate',data:values})}>
<Option value='1'>添加</Option>
<Option value='2'>删除</Option>
<Option value='3'>修改</Option>
</Select>
</div>)
}
}
export default connect(
state=>{
const {auth} = state
return {
auth,
}
},
dispatch=>{
return {
dispatch,
}
}
)(RoleInfo)
性能优化
- 阻止组件的重复渲染 .
- 衍生数据的重复计算 .
参考资料
redux 官网
redux 中文网
react-redux 官网
阮一峰 的redux 相关教程
redux 官方视频教程
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!