项目中应用到了redux,根据前人的代码和网上百度到的资料总结如下,以免遗忘。
简介
redux是一个状态管理工具,随着前端功能的增加,业务的复杂,将数据提取出组件是更好的方式,rudex是其中一种解决方案。
redux基本概念
redux由store、action、reducer 三部分组成。
store
store:仓库,是一个包括项目所有的状态数据的对象,redux提供了createStore函数来创建一个store
const store = createStore(reducer); //参数reducer下面介绍
action
action:动作,是一种包括类型和数据的对象,是reducer函数的参数,为reducer提供参数来进行不同的处理,典型形式是:
{
type:"ADD_CART_NUM",
data: data
}
reducer
reducer:处理函数(我的翻译),是一个具体的业务处理函数,位于action和disptch之间,被dispatch调用,根据传入action对象数据进行处理并返回一个新的state。典型形式是:
// 通过判断Action的类型,返回新的数据改变后的state对象,即使没有任何状态的改变,也要返回一个对象
// 注意:返回的state就是reducer提供的,也就是组件同步到的state
function counter(state = initState, action) { //state是reducer提供的默认值
const count = state.count
switch (action.type) {
case ADD_CART_NUM:
return { count: count + action.data.plusCount} //action.data是组件传入的的值
default:
return state
}
}
dispatch
dispath: 分发函数(我的翻译),是store内部的函数,传入action并调用reducer,一般形式为:
<button onClick={()=>store.dispatch(myAction.increaseCartNum())}>Increase</button>
//其中increaseCartNum返回一个action对象,在dispath内部进行对应reducer的调用
subscribe
subscribe: 注册监听函数(我的翻译),也是store内部的函数,用于组件同步store中数据,一般形式为:
componentWillMount(){
// 订阅状态变化
store.subscribe((state)=>this.setState(state)) //注册到监听器函数到store中
}
问题 store中是如何区分不同组件的state呢?
redux流转流程
假设store中数据位于上层,组件位于下层,那么有数据上行和数据下行两种流转。
数据下行
store通过props传递到具体组件,组件通过this.props.xxx拿到数据。
数据上行
组件中点击按钮等操作调用store的dispatch函数(对应的action作为函数参数),dispatch函数内部调用相应的recuder函数,改变并返回store数据
react-reduce
在react应用中,利用react-redux来对两者进行桥接使用。
react提供了一个函数和一个组件,简化redux流程
connect函数:实现同步store数据到组件的props中、暴露action函数隐藏对dispatch调用reducer。一般形式为
// 绑定store中的状态到组件的props
const mapStateToProps = state => {
return {
state
}
}
//暴露action到组件props
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(MiniCart) //其中MiniCart是组件
Provider标签用于将store传递到每个组件中,一般形式为:
ReactDOM.render(
<Provider store={store} >
<Router>
<LocaleProvider locale={zh_CN}><App /></LocaleProvider>
</Router>
</Provider >
, document.getElementById('root'));
整体代码
//action.js aciton文件
export function changeCartNum(data) {
return {
type: 'CART_NUM',
data
}
}
//frontReducer.js 子reducer文件,不同模块分为不同reducer文件
const init = { //reducer的初始状态
}
const Front = (state = init, action = {}) => {
switch (action.type) {
case 'CART_NUM':
//这里可以添加一些处理函数,
return { ...state, ...action.data }
}
}
export default Front
//reducers.js 总reducers文件
import { combineReducers } from 'redux'
import Front from './FrontReducer';
const reducer = combineReducers({ //合并reducer文件
Front,
xxx,//不同的reducer文件
xxx,//不同的reducer文件
})
export default reducer
//index.js文件
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store} > //传递store到内部组件中
<Router>
<LocaleProvider locale={zh_CN}><App /></LocaleProvider>
</Router>
</Provider >
, document.getElementById('root'));
//B组件获取cartNumber
import { changeCartNum } from '@actions';
class PublicTop extends React.Component {
render(){
....
<span >
购物车数量
{
this.props.state.Front.cartNum
}
</span>
.....
}
}
const mapStateToProps = state => {
return {
state
}
}
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(MiniCart)
//A组件设置cartNumber
import { changeCartNum } from '@actions'; // 购物车数量
class Cart extends Component {
render(){
.......
//增加购物车数量按钮的点击处理函数
this.props.changeCartNum({ cartNum: totals })//totals是设置的值
.......
}
}
const mapStateToProps = state => {
return {
state
}
}
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(Cart)
redux原生操作
以下程序是没有使用react-redux的原生操作,包括store的简易实现,用于理解redux的实现。
//reducer文件 counterReducer.js
// 提供一个初始的状态
initState={
count: 0
}
// 通过判断Action的类型,返回新的数据改变后的state对象,即使没有任何状态的改变,也要返回一个对象
export default function counter(state = initState, action) {
const count = state.count
switch (action.type) {
case INCREMENT:
return { count: count + 1 }
default:
return state
}
}
//主文件 index.js
// 创建一个store全局管理state和操作
const store = createStore(reducer);
// Provider在根组件<App>外面包了一层,App的所有子组件就默认都可以拿到store,通过组件的props传递
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<App/>
</Provider>
)
}
}
// createStore的简单实现
function createStore = ( reducer ) => {
let currentState; // 内部的状态
let listeners = []; //所有的监听者
const getState = () => currentState; // 获取store中的state
// dispatch的操作就是内部执行reducer()函数,action和reducer在这儿产生交集,并且通知所有的监听者
const dispatch = ( action ) => {
currentState = reducer(state, action); // 更新state
listeners.forEach(listener => listener());
}
// 订阅事件
const subscribe = ( listener ) => {
listeners.push(listener);
return ()=>{
listeners = listeners.filter(l => l !== listener)
}
}
return {
getState,
dispatch,
subscribe
}
}
//组件文件
class Counter extends Component{
componentWillMount(){
// 订阅状态变化
store.subscribe((state)=>this.setState(state)) //注册到监听器函数到store中
}
render() {
return (
<div>
<span>{value}</span>
//点击后dispatch事件类型
<button onClick={()=>store.dispatch(increaseAction.increase())}>Increase</button>
</div>
)
}
}