Redux
redux 是 JavaScript 状态容器,提供可预测化的状态管理。 可以用在React、Vue等前端库中。 react-redux是react中的redux插件,简化了redux的用法,使开发者更方便地使用redux来管理状态。
redux: 进行状态统一管理的类库(适应于任何技术体系的项目).
应用场景
1.只要两个或者多个组件之间想实现信息的共享,都可以基于redux解决,把共享的信息存到redux容器中进行管理.
2.还可以使用redux做临时存储: 页面加载的时候,把从服务器获取的数据信息存储到redux中,页面组件渲染需要的数据,从redux中获取,这样只要页面不刷新,路由切换的时候,再次渲染组件不需要重新从服务器中拉取数据,直接从redux获取即可;页面刷新,从头开始
为什么要有redux?
react中的数据流是单项传递的,即状态只能从父组件传递给子组件,虽然可以用ref在父调中调用子组件的方法从而返回子组件的状态给父组件使用、或者使用contex跨级传递属性等方法,但是当组件很多而且都需要共享一个属性的时候,那些方法管理属性就显得不规范且不方便,因此,需要使用redux这个专门的状态容器来统一管理状态。
Redux 的设计思想
设计思想很简单,就两句话:
1、Web 应用是一个状态机,视图与状态是一一对应的。
2、所有的状态,保存在一个对象里面。
redux暴露的几个方法
createStore
createStore: 创建容器,需要把reducer传递进来。createStore返回四个方法:
-> dispatch: 派发行为(传递一个对象,对象中有一个type属性),目的是通知reducer修改状态信息
-> subscribe: 给事件池追加方法
-> getState: 获取最新管理的状态信息
-> replaceReducer: 替换reducer(一般不用)
let store = createStore(reducer);
redux+hook实现n,m的++
import React,{ Component,useState } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
/*
* reducer的作用:
1.记录了所有状态修改的信息(根据不同的标识去操作和修改任务)
2.修改容器中的状态信息
* reducer的参数: reducer(state,action);
state: 原有的状态信息
action: 任务派发(dispatch)的时候,传递的行为对象(这个对象中必须有一个type属性==操作的行为标识(reducer就是根据这个行为标识修改状态信息的))
* reducer一定得把state给return出去.(否则getState()执行的时候取不到)
*/
let reducer = (state={ n:0,m:0 },action)=>{
switch(action.type){
// CHANGE_N和CHANGE_M就是行为标识
case 'CHANGE_N':
state = {...state,n: state.n+1};
break;
case 'CHANGE_M':
state = {...state,m: state.m+1}
break;
default:
break;
}
return state; //这里一定要把state返回.
}
// 1.创建容器,传入reducer
let store = createStore(reducer);
class App extends Component{
changeM = ()=>{
store.dispatch({
type: 'CHANGE_N'
})
}
changeN = ()=>{
store.dispatch({
type: 'CHANGE_M'
})
}
render(){
return(
// {/* React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。 */}
<React.Fragment>
<ComN store={ store }></ComN>
<ComM store={ store }></ComM>
<ComBtn
store={ store }
changeM={ this.changeM }
changeN={ this.changeN }>
</ComBtn>
</React.Fragment>
)
}
}
// 组件N
function ComN(props){
let {store:{ getState,subscribe }} = props;
let { n } = getState();
// 使用useState创建函数中的状态信息
let [nValue,setNValue] = useState(n);
subscribe(()=>{
let { n } = getState();
setNValue(n);
})
return(
<div>n的值: { nValue } </div>
)
}
// 组件M
function ComM(props){
let { store:{ getState,subscribe} } = props;
let { m } = getState();
let [mValue,setMValue] = useState(m);
subscribe(()=>{
let { m } = getState();
setMValue(m);
})
return(
<div>m的值: { mValue } </div>
)
}
// 改变N,M值的组件
function ComBtn(props){
let btn = {
marginTop: '30px',
display: 'inline-block',
width: "100px",
height: "40px",
border: "1px solid #ccc",
textAlign: 'center',
lineHeight: '40px',
}
return(
<div>
<div style={ btn } onClick={ ()=>{
props.changeN() } }>改变 n++</div>
<div style={ btn } onClick={ ()=>{
props.changeM() } }>改变 m++</div>
</div>
)
}
ReactDOM.render(<App />,document.getElementById('root'));
combineReducers
combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。
import { combineReducers } from 'redux';
import reducerA from './reducerA';
import reducerB from './reducerB';
const reducer = combineReducers({
reducerA,
reducerB
});
export default reducer;
bindActionCreators
applyMiddleware
应用中间件applyMiddleware
import { createStore,applyMiddleware } from 'redux';
import reducer from './reducer';
import reduxLogger from 'redux-logger';
import reduxThunk from 'redux-thunk';
import reduxPromise from 'redux-promise'
// 应用中间件applyMiddleware
let store = createStore(reducer,applyMiddleware(reduxLogger,reduxThunk,reduxPromise));
compose
redux工程化项目目录结构
-> store
-> index.js // 创建容器
-> action-types.js // 所以派发任务的行为标识都存与这里统一管理
-> reducer // 存放每一个模块的reducer
-> reducerA.js // reducerA模块
-> reducerB.js // reducerB模块
-> ... // 其他模块
-> index.js // 把每一个模块的reducer最后合并成为一个reducer
-> action // 存放每一个模块需要进行的派发任务(ActionCreator)
-> reducerA.js // reducerA模块
-> reducerB.js // reducerB模块
-> ... // 其他模块
-> index.js // 所有的Action进行合并
action-types.js 派发任务的标识
// reducerA模块下的任务标识
export const REDUCERA_CHANGE_N = 'REDUCERA_CHANGE_N';
export const REDUCERA_CHANGE_M = 'REDUCERA_CHANGE_M';
// reducerB模块下的任务标识
export const REDUCERB_CHANGE_N = 'REDUCERA_CHANGE_N';
export const REDUCERB_CHANGE_M = 'REDUCERA_CHANGE_M';
reducer目录
reducerA模块
import * as ActionTypes from '../action-types.js';
export default function reducerA(state={
n: 0,
m: 0
},action){
switch(action.type){
case ActionTypes.REDUCERA_CHANGE_N:
state = {...state,n:state.n+1};
break;
case ActionTypes.REDUCERA_CHANGE_M:
state = {...state,m:state.m+1};
break;
default:
break;
}
return state;
}
reducerB模块
import * as ActionTypes from '../action-types.js';
export default function reducerB(state={
n: 0,
m: 0
},action){
...
return state;
}
reducer-> index.js 合并reducer
/*
* 一个项目中只能有一个reducer
*
* 把每个模块单独设定的REDUCER函数最后合并成为总的REDUCER,
* 为了保证合并REDUCER过程中,每个模块管理的状态信息不会相互冲突,redux在合并的时候把容器中的状态进行分开管理(已合并REDUCER时候设置的属性名作为状态划分的属性名,把各个模块管理的状态放到自己的属性下即可)
* 此时容器中的状态
* STATE = {
* reducerA:{
* n: 0,
* m: 0
* },
* reducerA:{
* n: 0,
* m: 0
* }
* }
*
* 以后获取状态信息的时候,也需要把reducerA等模块名加上
* store.getState().vote.reducerA;
*
* */
import { combineReducers } from 'redux';
import reducerA from './reducerA';
import reducerB from './reducerB';
let reducer = combineReducers({
reducerA,
reducerB
})
export default reducer;
store下的index.js
import { createStore } from 'redux';
import reducer from './reducer';
let store = createStore(reducer);
export default store;
Action目录
reducerA模块
/*
* 每个板块单独的ACTION-creator: 就是把DISPATCH派发时候需要传递的ACTION对象进一步统一封装处理
* */
import * as from ActionTypes from '../action-types';
let reducerA = {
changeN(){
return {
type: ActionTypes.reducerA_CHANGE_N
}
},
changeM(){
return {
type: ActionTypes.reducerA_CHANGE_M
}
}
}
export default reducerA;
reducerB模块
/*
* 每个板块单独的ACTION-creator: 就是把DISPATCH派发时候需要传递的ACTION对象进一步统一封装处理
* */
import * as from ActionTypes from '../action-types';
let reducerB = {
changeN(){
return {
type: ActionTypes.reducerB_CHANGE_N
}
},
changeM(){
return {
type: ActionTypes.reducerB_CHANGE_M
}
}
}
export default reducerB;
index.js合并
/*
* 合并所以的action-creator,类似于reducer合并,为了防止冲突,合并后的对象是以模块名称单独划分管理
*
* Action={
* reducerA:{},
* reducerB:{},
* }
* */
import reducerA from './reducerA'
import reducerB from './reducerB'
let action = {
reducerA,
reducerB
}
export default action;
利用工程化的redux渲染页面
import React,{ Component,useState } from 'react';
import ReactDOM from 'react-dom';
import * as ActionTypes from './store/action-types'
import action from './store/action/index';
import store from './store/index';
class App extends Component{
changeM = ()=>{
console.log(11);
store.dispatch(action.vote.changeM())
}
changeN = ()=>{
store.dispatch({
type: ActionTypes.VOTE_CHANGE_N
})
}
render(){
return(
// {/* React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。 */}
<React.Fragment>
<ComN store={ store }></ComN>
<ComM store={ store }></ComM>
<ComBtn
store={ store }
changeM={ this.changeM }
changeN={ this.changeN }>
</ComBtn>
</React.Fragment>
)
}
}
// 组件N
function ComN(props){
let {store:{ getState,subscribe }} = props;
let { n } = getState().vote;
// 使用useState创建函数中的状态信息
let [nValue,setNValue] = useState(n);
subscribe(()=>{
let { n } = getState().vote;
setNValue(n);
})
return(
<div>n的值: { nValue } </div>
)
}
// 组件M
function ComM(props){
let { store:{ getState,subscribe} } = props;
let { m } = getState().vote;
let [mValue,setMValue] = useState(m);
subscribe(()=>{
let { m } = getState().vote;
setMValue(m);
})
return(
<div>m的值: { mValue } </div>
)
}
// 改变N,M值的组件
function ComBtn(props){
let btn = {
marginTop: '30px',
display: 'inline-block',
width: "100px",
height: "40px",
border: "1px solid #ccc",
textAlign: 'center',
lineHeight: '40px',
}
return(
<div>
<div style={ btn } onClick={ ()=>{
props.changeN() } }>改变 n++</div>
<div style={ btn } onClick={ ()=>{
props.changeM() } }>改变 m++</div>
</div>
)
}
ReactDOM.render(<App />,document.getElementById('root'));
React-Redux
react-redux是把redux进一步封装,适配react项目,让react操作起来更简单
store文件夹中的内容和redux一模一样
在调取组件使用的时候可以优化一些操作步骤
react-redux暴露出的一些方法
Provider 根组件
当前整个项目都在Provider组件下,作用就是把创建的STORE可以提供内部任何后代组件使用(基于上下文完成的)
-> Provider组件中只允许出现一个子元素
-> 把创建的store基于属性传递给Provider(这样后代组件中都可以使用这个store了);
import React,{ Component,useState } from 'react';
import ReactDOM from 'react-dom';
import store from './store';
import { Provider } from 'react-redux';
import App from './App.js';
ReactDOM.render(<Provider>
<App />
</Provider>,document.getElementById('root'));
connect高阶组件
/*
* 相对于传统的REDUX,react-redux做的步骤优化
* 1.导出的不在是我们创建的组件了,而是基于connect构造后的高阶组件
* export default connect(mapStateToProps,mapDispatchToProps)(自己创建的组件)
* react-redux做了一件非常有作用的事情
* 以前我们需要手动基于subscribe向事件池中追加自己的方法,已达到容器信息改变,执行我们追加的方法,重新渲染组件的目的.
* react-redux帮我们做了这件事: 所有用到Redux容器状态信息的组件,都会向事件池追加一个方法,当状态信息改变,通知方法执行,吧最新的状态下信息作为属性传递给组件,组件属性改变,组件也会重新渲染.
*/
import React,{ Component } from 'react';
import { connect } from 'react-redux';
import action from './store/action/index';
class App extends Component{
render(){
return(
<React.Fragment>
<ComN}></ComN>
<ComM></ComM>
<ComBtn
changeM={ ()=>{ this.props.changeM() }}
changeN={ ()=>{ this.props.changeN() }}>
</ComBtn>
</React.Fragment>
)
}
}
// 组件M
function ComM(props){
return(
<div>m的值: { props.m } </div>
)
}
// 组件m
function ComN(props){
return(
<div>m的值: { props.n } </div>
)
}
/*
// 把redux容器中的状态信息遍历,赋值给当前组件的属性(state);
let mapStateToProps = (state) =>{
// => state: 就是redux容器中的状态信息
// 我们返回的是啥就把他挂载到当前组件的属性上(redux中存储很多信息,我们想用啥返回啥即可)
return {
...state.vote
}
}
// 把redux中的dispatch派发行为,也赋值给组件的属性(actionCreator)
let mapDispatchToProps = (dispatch)=>{
// => dispatch: Store中存储的DISPATCH方法
// => 返回的是啥就相当于吧啥挂载到组件的属性上(一般我们挂载一些方法,这些方法中我们完成了dispatch派发热舞操作)
return {
changeM(){
dispatch(action.changeM())
},
changeN(){
dispatch(action.changeN())
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(App);
*/
export default connect((state)=>{
return{
...state.vote
}
,action.vote)(App);// react-redux帮我们做了一件事情,把action-creator中编写的方法(返回ACTION对象的方法),自动狗线程dispatch派发热舞的方法,也就是mapDispatchToProps
Redux中间件
redux-logger
redux-thunk
redux-promise
redux-saga
总结:
细节之处,略有疏漏,如有不足或者错误的地方,欢迎大佬批评并加以指正.谢谢.