一、redux简介
官网称之为 JS 应用的状态容器,提供可预测的状态管理,并且可以与前端三大框架搭配,但是vue中有自己团队的vuex,一般与react配合使用,在我个人理解为,这可以提高数据可用性,当同一条数据在多个组件中都要调用时,这个时候就可以用状态容器进行存储调用或者对改状态进行操作
二、根据原理图进行解析
ReactComponents:当前需要使用状态管理的组件,在组件中需要引入store,通store.getState()获取state值,通过ActionCreators进行像store发送改变状态的内容
ActionCreators:用dispatch通知store要改变,action两个值:type(类型,根据type来判断如何加工数据),data(需要处理的值)
Store:存放处理好的数据。可以通过接收到新的dispatch像reducers派发更新任务
Reducers:根据接收到的任务,进行对state的改变,接收两个参数:previousState(前一个值),action就是dispatch中的那个参数,再根据type类型判断如何操作,最后把新的状态返回到store
如图

示例
下载npm install redux 在redux文件夹下
创建store.js
/*
该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './reducers/count.js'
//暴露store
export default createStore(countReducer)
创建action文件夹,在该文件夹下创建count.js
/*
该文件专门为Count组件生成action对象
*/
//同步action,就是指action的值为Object类型的一般对象
export const increment = data => ({type:'increment',data})
export const decrement = data => ({type:'decrement',data})
创建reducer文件夹,在该文件夹下创建count.js
/*
1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
// console.log(preState);
//从action对象中获取:type、data
const {type,data} = action
//根据type决定如何加工数据
switch (type) {
case 'increment': //如果是加
return preState + data
case 'decrement': //若果是减
return preState - data
default:
return preState
}
}
最后就是在component组件中调用
import React, { Component } from 'react'
//引入store,用于获取redux中保存状态
import store from '../../redux/store'
//引入actionCreator,专门用于创建action对象
import {
createIncrementAction,
createDecrementAction
} from '../../redux/actions/count'
export default class Count extends Component {
state = {carName:'奔驰c63'}
//加法
increment = ()=>{
const {value} = this.selectNumber
store.dispatch(createIncrementAction(value*1))
}
//减法
decrement = ()=>{
const {value} = this.selectNumber
store.dispatch(createDecrementAction(value*1))
}
render() {
return (
<div>
<h1>当前求和为:{store.getState()}</h1>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
注意,此时状态是改变了,但是页面没有跟新
可以 通过subscribe订阅监听store中值的改变,在利用react中的setState特性驱动页面更新,如下
componentDidMount(){
//检测redux中状态的变化,只要变化,就调用render
store.subscribe(()=>{
this.setState({})
})
}
如果是异步加载,需要下载redux-thunk依赖
首先在store中引入,在createStore第二个参数中添加 applyMiddleware(thunk)
import {createStore,applyMiddleware} from 'redux'
import countReducer from './reducers/count.js'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer,applyMiddleware(thunk))
在action文件下的count.js中添加,在component中使用与同步的一样,只不过多了一个时间参数
//异步action,就是指action的值为函数,异步action中一般都会调用同步action
export const createIncrementAsyncAction = (data,time) => {
return (dispatch)=>{
setTimeout(()=>{
dispatch(createIncrementAction(data))
},time)
}
}
注意:reducer中的函数都是纯函数,不能直接更改传入的值然后返回,这样页面不会跟新,需要用一个新的参数接收返回,然后返回,案例如下
import {ADD_PERSON} from '../constant'
//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]
export default function personReducer(preState=initState,action){
// console.log('personReducer@#@#@#');
const {type,data} = action
switch (type) {
case ADD_PERSON: //若是添加一个人
//preState.unshift(data) //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。
return [data,...preState]
default:
return preState
}
}
这就是一个简单完整的demo
三、 react-redux
顾名思义,基于react封装的,主体操作模型如下
1、所有的ui组件都是包裹在一个容器组件,他们是父子关系
2、容器组件可以正常使用redux的api,而ui组件不能直接使用,容器组件会用porps的方式把redux中所保存的状态和用于操作的方法传给ui组件
如下图

对了,react-redix是不需要subscribe来监听state的变化,能自动响应state的变化,然后重新渲染render
基础用法如下(本用法是基于上一个redux之上的)
首先在父组件中引入store传入父组件, 为方便组件后代都能接收到store,省的在每个父组件中引入store,我就直接在最外面的最外面的index.js中用Provider处理了
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'
ReactDOM.render(
/* 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
)
然后编写 容器组件 和 ui组件 可以把容器组件 和 ui组件 集成在一个jsx文件中
import React, { Component } from 'react'
//引入action
import {
increment,
decrement
} from '../../redux/actions/count'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'
//定义UI组件
class Count extends Component {
//加法
increment = ()=>{
const {value} = this.selectNumber
this.props.increment(value*1)
}
//减法
decrement = ()=>{
const {value} = this.selectNumber
this.props.decrement(value*1)
}
render() {
//console.log('UI组件接收到的props是',this.props);
return (
<div>
<h4>当前求和为:{this.props.count}</h4>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
/*
1.使用connect()()创建并暴露一个Count的容器组件
2.connect 两个参 mapStateToProps 和 mapDispatchToProps
mapStateToProps:
1.mapStateToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
3.mapStateToProps用于传递状态
mapDispatchToProps:
1.mapDispatchToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
3.mapDispatchToProps用于传递操作状态的方法
*/
export default connect(
state => ({
count:state
}),
{increment,decrement} //传入action
)(Count)
最后
可以把所有的reduce汇众到一个文件,然后传入combineReducers函数,在引入store中,这样就可以用state.count 或某一个值得到不同的数据,替代原先的countReducer
在reducer文件夹下新建 index.js文件
/*
该文件用于汇总所有的reducer为一个总的reducer
*/
//引入combineReducers,用于汇总多个reducer
import {combineReducers} from 'redux'
//引入为Count组件服务的reducer
import count from './count'
//引入为Person组件服务的reducer
import persons from './person'
//汇总所有的reducer变为一个总的reducer
export default combineReducers({
count,
persons
})