一、react的状态管理工具简介
redux是一种状态管理工具,它的作用就是能管理状态呗。其实它本质上就是一个全局对象,这个全局对象能被各个组件访问,并且各个组件能操作这个对象(以单向数据流的方式)来读取修改数据等。
- 其他的还有: flux(这个已经很老了) 、mobx(相对比较新),可以自行了解。
二、redux使用介绍
1、官网介绍:redux官网
2、阮一峰博客文档(推荐):阮一峰redux基本用法介绍 这里面还有一张图,非常好理解,我把它单独拿出来随时观摩观摩。
注意!!!本文写了不少种方法。
- 标题
三、实际使用基础步骤
是最原始的方法。四、简单优化redux使用。
是对三、实际使用基础步骤
的简单优化。 - 标题
三
、四
、五
、六
这个两个版本其实是七(插件版)
原理。有些复杂和杂乱,强烈建议跳过五
、六
,先看七
。要是还有余力再看五
、六
。(重要)
三、实际使用基础步骤
本步骤将以todolist为背景:先新建好脚手架,建立components文件夹,再创建todo文件夹(作为todolist模块),创建ToDo.js组件,并在app.js引用组件。
1、安装redux,输入命令npm i redux --save
2、存数据
在components里面新建store文件夹(作为store模块),在store里面新建Store.js 、 Reducer.js 文件。
(1)Reducer.js里面写如下代码:
// initstate存放最初的数据,根据功能自行定义
var initState ={
list:[{id:1,text:'aa'},{id:2,text:'bb'}],
genId:2
}
// reducer 有两个参数:
// state =初始值 state 修改的数据的当前状态 只读,要创建副本 原理:function dou(a=0) {return a*2}
// action 动作 是一个对象 必须有 type属性 必须返回状态
export const reducer = (state =initState , action) => {
switch (action.type) {
case 'ADD':
// 以下代码根据实际需求修改,注意,state是只读属性,必须建立副本来使用
var newState = {...state};
newState.genId++;
newState.list.push({id:newState.genId,text:action.text}) ///这个action.text是修改数据的时候传的值,在后面第 4 步有讲。
return newState;
default:
return state
}
}
(2)在Store.js里面引入redux和reducer,创建并导出store。
import {createStore} from 'redux';
import {reducer} from './reducer'
var store = createStore(reducer);
export default store;
3、取数据
(1)引入storeimport store from '../store/store';
。
(2)通过 store.getState().state的变量
取数据。
constructor() {
super();
this.state = {
list:store.getState().list, //取数据
str:''
}
}
4、改数据
(1)在你的模块里面(本例是todo模块),新建文件ActionCreator.js,在里面写如下代码:
export default {
// 这个名字是随便取的,text这个参数课传可不传
gipAction(text){
return {
type:"ADD",
text // es6新写法,等价于text:text
}
}
}
(2) 在你的模块里面(本例是todo模块) 引入ActionCreator.jsimport actionCreator from './actionCreator'
。
(3)抛发动作:store.dispatch(动作)
store.dispatch(actionCreator.gipAction(this.text.value))
注:reducer 将会判断动作类型(type)、创建state的副本、修改、返回新状态
(4)监听数据变化,store.getState().变量
拿到最新状态,然后在组件里设置订阅subscribe。
constructor() {
super();
this.getInputValue = this.getInputValue.bind(this);
this.state = {
list:store.getState().list,
str:''
}
//监控store里的数据变化
store.subscribe(this.sub.bind(this))
}
sub(){
this.setState({
list:store.getState().list
})
}
本案例代码:todolist案例
四、简单优化redux使用。
1、分模块使用reducer
(1)把以前写在store里面的reducer放在todolist页面。
var initState ={
list:[{id:1,text:'aa'},{id:2,text:'bb'}],
genId:2
}
export const todoReducer = (state =initState , action) => {
console.log(action)
switch (action.type) {
case 'ADD':
var newState = {...state};
newState.genId++;
newState.list.push({id:newState.genId,text:action.text})
console.log(newState);
return newState;
default:
return state
}
}
(2)把store里面新建reducers模块,作用是:总和todolist或其他模块。
import {combineReducers} from 'redux';
import {todoReducer} from '../todolist/reducer'
var reducer = combineReducers({
todo:todoReducer
})
export default reducer;
(3)把模块里面取数据的加上模块名。
constructor() {
super();
this.state = {
list:store.getState().todo.list //取数据时加上模块名(store里面导入时的模块名字)
}
}
2、把方法名单独列到文件里。
(1)在store文件夹里面,新建actionType.js,写如下内容:
export const ADD= 'ADD';
export const DEC = 'DEC';
(2)改对应的写方法名的地方。createAction、reducer里面。
- 在文件顶部一引入actionType,
import {ADD,DEC} from '../../store/actionType'
。 - 把写了
"ADD"
字符的地方改为ADD
变量就行了。
五、终极优化(组件手工拆分版)
要跟store打交道,又要渲染。有点杂乱。所以目标是把组件拆分成两部分,容器组件和ui组件。
六、最终极优化(context版)
每个个文件都会引入store,就很烦。那么我们怎么解决呢?用context(上下文)来传。
- createContext() 返回值是context,包含两个对象 Provider 、 Consumer
- yinr
首先要引入和创建context。
生产者和消费者
新建文件MyProvider
在index里面引入,并包裹
七、最最终极版本(插件版react-redux)
用这个react-redux这个插件有什么好处呢?它帮你做完了三个事情:
- 自动完成subscribe订阅
- 完成容器组件的功能
- context传递store帮你做完了。
使用步骤:
1、安装插件:
npm i react-redux --save
2、准备工作:
以下是在store模块里:
(1)建立store文件夹,新建store.js
import {createStore} from 'redux'
import reducers from './reducers' //总的reducer
var store = createStore(reducer);
export default store;
(2)在store文件夹里新建reducers,并记得引入combineReducers。
import {combineReducers} from 'redux'
import {counterReducer} from '../components/counter/reducer' // 自己的模块
var reducer = combineReducers({
counter:counterReducer // 自己的模块
})
export default reducer;
以下是在counter模块里:
(3)在components建立counter文件夹,新建reducer.js。写初始值和写counterReducer。
import {INC,DEC} from '../../store/actionType'
var initState = {
one: 1,
two: 2,
three: 3
};
export const counterReducer = (state = initState, action) => {
var newState = { ...state };
switch (action.type) {
case INC:
newState[action.name]++;
return newState;
case DEC:
newState[action.name]--;
return newState;
default:
return state;
}
};
(4)记得在store里面的reducers里面引入counter模块的reudcer。
import {combineReducers} from 'redux'
import {counterReducer} from '../components/counter/reducer' // 引入的counter
var reducer = combineReducers({
counter:counterReducer // counter模块
})
3、引入和使用Provider:
(1)在index.js引入react-redux、store。把App组件用Provider包裹。
import {Provider} from 'react-redux'; // 引入react-redux
import store from './store' // 引入store
ReactDOM.render(
<Provider store={store}> //用Provider把App组件包裹起来,把store传进去。
<App />
</Provider>, document.getElementById('root'));
serviceWorker.unregister();
(2)在counter里面写自己的组件,建立Counter组件。就可以把数据渲染出来了。
import React, { Component } from 'react'
import {connect} from 'react-redux'
import actionCreator from './actionCreator';
class Counter extends Component {
render() {
let {inc,name,dec} = this.props;
return (
<div>
<button onClick={dec.bind(this,name)}>-1</button>
{this.props.v}
<button onClick={inc.bind(this,name)}>+1</button>
</div>
)
}
}
var mapDispatch ={
inc:actionCreator.incAction,
dec:actionCreator.decAction //把actionCreator里的方法进行改名
}
export default connect(null,mapDispatch)(Counter);
(3)