React状态管理
1.作用:实现多组件状态共享,也是组件通信的一个方案
2.React状态管理
-Flux 目前很少人用了 是其他状态管理的基础
-Redux 最流行的形式
-mobx 前卫的形式
3.React是一个视图层框架,可以认为是单纯的MVC中的V
Flux
Flux是一种软件架构思维,后期发展改进,诞生了Redux,在Flux的架构思维中,React只是其中的一部分,V
Flux的组成部分
- View:视图层(React)
- ActionCreators(动作创建者):视图层发出的消息(如点击事件)
- Dispatcher(派发器):用来接收Actions、执行回调函数
- Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面
流程想要使用Flux架构思维需要通过一个工具进行使用,flux
1.安装 yarn add flux
2.在src目录下 新建store目录,里面新建index.js
-store有两个功能
-存放数据
-当数据发生改变时,视图要进行更新(当前组件中的state发生了改变,从新从store中获取数据,要想重新复制,那么要通过事件的发布,订阅)
store>index.js的内容
/*
store是Flux的一个组成部分
功能
1.存储状态
2.更新视图 ->事件发布 - 订阅 -> Node的envents模块来实现
*/
const events = require('events')
//1.创建store模块
const store = {
...events.EventEmitter.prototype,//给store 添加属性 on emit 让store具有订阅和发布的功能
state:{//定义一个状态
count:0
},
getState(){//获取状态
return this.state.count
}
}
export default store
//将store模块引入需要使用状态的文件(A.js),通过store.getState()这个方法给A.js里的状态赋值,
//然后进行数据展示
action_creators>index.js内容
/*
action
用于将视图的逻辑迁移到flux的acttionCreators身上
action都是方法,他的作用是创建action
*/
import {
INCREMENT,
} from './actionType'
import dispatcher from '../dispatcher'
export const add = () => {
//1.创建动作
console.log('action')
const action = {
type:INCREMENT
}
//2.发送动作 action是不能发动的,得由dispatcher来发送
dispatcher.dispatch( action )
}
dispatcher>index.js内容
/*
dispatcher
作用: 修改数据
*/
import { Dispatcher } from 'flux'
import { INCREMENT } from '../actions_creators/actionType'
import store from '../store'
//1.得到dispatcher
const dispatcher = new Dispatcher()
//2.dispatcher要想使用得注册
//dispatcher.register(callback)
dispatcher.register((action) => {//actions中的动作
//判断是哪一个动作,对应的做数据修改
switch (action.type) {
case INCREMENT:
console.log('dispatcher')
store.state.count ++
break;
default:
break;
}
})
export default dispatcher
视图内容
import React,{ Fragment,useState,useEffect } from 'react'
import './index.scss'
import store from '../../store'
import {
add
} from '@/actions_creators'
class HotSale extends React.Component {
constructor( props ){
super(props)
this.state = {
count:store.getState() //1.视图初始化状态(从Flux得到状态)
}
}
increment = () => {//2.视图进行相应的操作
add() //Flux的action_creators创建相应操作的动作,dispatcher发送动作,然后将相应的动作导入
store.emit('addHandler') //4.store.emit来发布订阅的事件来更新视图
}
componentDidMount(){ //3.通过store.on来订阅事件来准备更新视图
store.on('addHandler',() => {
this.setState({
count:store.getState()
})
})
}
render(){
return(
<Fragment>
<div className='hotsale-box'>
<button onClick = { this.increment }>+</button>
<p>count:{ this.state.count }</p>
</div>
</Fragment>
)
}
}
export default HotSale
redux
flux + 函数式编程 = redux
redux核心组成部分
- store 数据管理者和存储者
- actionCreators 动作创建者,发送动作给reducers
- react Components 组件(视图层)
- reducers 数据的修改者,返回一个新的 newstate 给 store
Redux的设计思想
- Web应用是一个状态机,视图于状态是一一对应的。
- 所有状态,保存在一个对象里(唯一数据源)
注意:flux、redux都不是必须和react搭配使用的,因为flux和redux是完整的架构,这学习react的时候,只是将react的组件作为redux中的视图层去使用
Redux的使用的三大原则:
- SIngles Source of Truth(唯一数据源)
- State is read-only(状态是只读的)
- changes are made with pure function(数据的改变必须通过纯函数完成)
数据不分块
安装redux
-yarn add redux
在src下面创建store文件夹,里面创建index.js
store>index.js
/*
数据的管理者和存储者
*/
import { createStore } from 'redux'
import rootReducer from '../reducers' //在reducers里创建 rootReducer
const store = createStore( rootReducer )
export default store
reducers index.js
/*
reducer是一个纯函数
接收两个参数 previousState action
previousState初始状态
action是actions发送来的动作
*/
import { //引入动作类型
INCREMENT,
ADDTODOS
} from '../actions/actionType'
const state = {//这是初始值 这个值只读,不能改
count: 0,
todos:[
{
id:1,
task:'任务一'
},
{
id:2,
task:'任务二'
}
]
}
const rootReducer = (previousState = state, action) => {
/* state是只读的,不可变的,那么previousState也不能改 */
//这里使用克隆 深浅都可以 深克隆最保险 深克隆使用 loadsh_.cloneDeep/Immutable.js来完成
const newState = {//浅克隆
...previousState
}
//判断用户做了什么动作
switch (action.type) {
case INCREMENT:
//用户操作->修改数据
newState.count++
break;
case ADDTODOS:
//用户操作->修改数据
newState.todos.push({
id:newState.todos.sort((a,b) => {
return b.id - a.id
})[0].id+1,
task:action.payload //视图传递来的参数
})
break;
default:
break;
}
// console.log(newState)
return newState //返回一个新的状态 store.getState可以拿到里面的数据
}
export default rootReducer
action>index.js
import { INCREMENT,ADDTODOS } from './actionType'
import store from '../store'
export const increnment = () => {
// console.log('action')
const action = {//创建动作
type:INCREMENT
}
//发送动作
store.dispatch(action)
}
export const addtodos = (val) => {//视图传递来的参数
const action = {
type:ADDTODOS,
payload:val
}
store.dispatch(action)
}
上面操作过后需要更新视图
在所在组件的componentDidMount里 使用事件订阅 store.subscribe(()=>{ this.setState()})来更新视图
数据分块
1.技术栈
1.redux
2.react-redux 实现数据分块的
3.redux 中间件
-redux-thunk 数据请求
-redux-saga 项目变大了 数据请求用这个 解决rdux-thunk数据请求异步回调问题
-redux-promise
新建store文件夹对数据统一管理(唯一数据源),store>index.js 存储数据
import { createStore,applyMiddleware } from 'redux'//用来创建store的,thunk方这里面
import thunk from 'redux-thunk' //用来数据请求的 得先有thunk才能做数据请求
import rootReducer from '../reducers'
const store = createStore( rootReducer,applyMiddleware(thunk) )
export default store
action>index.js 请求数据
还需要创建动作类型文件
export const GET_MOVIES = 'GET_MOVIES' //创建 相应操作对应的动作类型
--------------------------------------------------------------------------
import request from '../utils/request'
import { GET_MOVIES } from './actionType'
export const getMovies = () => {//redux-thunk 要求稳返回值是一个函数,函数参数是dispatch
return async dispatch => {
const result = await request({
method:'get',
url:'/ajax/movieOnInfoList',
params:{
token: ''
}
})
const action = { //创建动作
type:GET_MOVIES, //动作类型 用户的操作对应相应的动作类型
payload:result.data //动作的结果
}
dispatch( action ) //发送动作
}
}
reducers>index.js
import { combineReducers } from 'redux'
import homeReducer from './homeReducer'
const rootReducer = combineReducers({
home:homeReducer //将分块数据进行管理
})
export default rootReducer
reducers>homeReducer(某一块数据)修改数据
import { GET_MOVIES } from '../actions/actionType'
const initState = { //定义初始化数据
data:null
}
const homeReducer = (state = initState,action) => {
const newState = { //因为数据只可读,所以浅拷贝一层,深拷贝保险但是 性能差
...state
}
switch (action.type) { //对应action里的动作 进行相应的操作
case GET_MOVIES:
newState.data = action.payload //将action请求到的数据赋值给浅拷贝的数据
break;
default:
break;
}
return newState //将浅拷贝的数据返回出去store就能拿到了
}
export default homeReducer
然后再index.js根目录文件里 用上下文包裹住根组件
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux' //上下文组件
import store from './store' //数据源
ReactDOM.render(
<Provider store = { store }> //被包裹的组件的子组件都可以使用 connect组件
<App />
</Provider>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
然后再相应的视图组件内使用容器组件
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { getMovies } from '../../actions'
/*
connect会根据UI组件自动生成一个容器组件,
可以帮助我们获取store里面的数据和action里面的方法,
然后通过属性自动绑定到UI组件身上,它还可以帮助我们更新视图
*/
class Home extends Component {
componentDidMount(){
this.props.getMovies() // 这样就能拿到数据了
}
render() {
return (
<div>
</div>
)
}
}
const mapStateFromProps = state => {
return {
data:state.home.data //ui组件的props中就有data了
}
}
const mapDispatchFromProps = dispatch => {
return bindActionCreators({getMovies},dispatch)
}
export default connect (mapStateFromProps,mapDispatchFromProps)(Home) //将action里的方法绑定到ui组件身上
/*
connect两个参数
mapStateFromProps 将store里面的state转换成props,然后home组件继承容器组件的属性
mapDispatchFromProps 将action里面的方法绑定在UI组件身上,以props的方式访问,然后帮我们发送action给redux
*/
Mobx
actions,事件调用,actions是唯一可以修改state的东西并且可能有其他副作用 **数据修改者**
state, 数据 **数据存贮者**
component value 可以使用函数式组件从state中导出值 **数据更新的结果**
mobx会自动更新并在它不再使用时将其优化
reacts视图更新 **数据展示者**
使用mobx-react进行数据分块,使用mobx可以定义一个新的生命周期函数componentwillreact
-
创建项目-create-react-app app
-
进入项目 cd app
-
进行配置文件抽离-yarn eject
-
安装mobx - yarn add mobx mobx-react
如果有git冲突 我们要先将原文件放在本地暂存盘 git add . git commit -m '' 然后安装mobx mobx-react 注意不要
5.配置装饰器(修饰器es6)babel 解析装饰器
yarn add babel-plugin-transform-decorators-legacy -D yarn add @babel/preset-env -D yarn add babel-plugin-transform-class-properties -D yarn add @babel/plugin-proposal-decorators -D
6.配置peage.json
"babel": {
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
"transform-class-properties"
],
"presets": [
"react-app",
"@babel/preset-env"
]
},
//注意: 以下两个配置顺序不可更改
// [
// "@babel/plugin-proposal-decorators",
// {
// "legacy": true
// }
// ],
// "transform-class-properties"
在项目中的应用-分块
src 新建store目录=> index.js
用来打造store
导入下面打造的分片
import count from '分块路径'
const store = {
//在store文件夹里打造分片的数据
count
}
export default store
在store文件夹里打造分片的数据
import {
observable,
action
}
class Count {
@observable //定义可观察的状态
num 0
@action //定义用户交互的方法
increment(){
this.props.store.count.num ++
}
}
const count = new Count()
export default count
然后在index.js里从 'mobx-react’里导出 Provider
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'mobx-react' //上下文组件
import store from './store' //数据源
ReactDOM.render(
<Provider store = { store }> //被包裹的组件的子组件都可以使用 connect组件
<App />
</Provider>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
在视图组件使用 inject
import React,{ Component } from 'react'
import { inject,observer } from 'mobx-react'
@inject('store')//inject也是一个函数,这个函数接收'store'作为参数,可以将store里的数据和方法给到组件身上
@observer
class Count extends Component {
render(){
//通过inject注入store中的api,我们发现actions中的方法不会在this中显示,
const { num,increment } = this.props.store.count
return(
<div>
<button onClick = { increment.bind(this) }> + <button>
<p>count:{ num }</p>
</div>
)
}
}
export default Count
mobx流程
在store.index中创建store,在store中存入分片,状态分片的内容为,定义的状态和改变状态的方法,通过上下文组件包裹根组件,在需要的视图组件里使用@inject(store),然后该组件的this.props。store.状态分片,就能访问到装和方法。注意!!!!方法是console不出来的,建议在方法中写一个console来判断是否已经拿到方法