一、Mobx
(1) Mobx是一个功能强大,上手非常容易的状态管理工具。
(2) Mobx背后的哲学很简单: 任何源自应用状态的东西都应该自动地获得。只获取与自己相关的数据,不获取无关数据(redux则相反)
(3) Mobx利用getter和setter来收集组件的数据依赖关系,从而在数据发生变化的时
候精确知道哪些组件需要重绘,在界面的规模变大的时候,往往会有很多细粒度更新,提升性能.
二、Mobx与redux
Mobx写法上更偏向于OOP(面向对象)。
对一份数据直接进行修改操作,不需要始终返回一个新的数据。
并非单一store,可以多store。
Redux默认以JavaScript原生对象形式存储数据,而Mobx使用可观察对象
三、mobx安装:npm install mobx@6
四、mobx使用
1.普通数据类型和复杂数据类型使用案例
不加装饰器的话只能处理同步,处理不了异步
import React, { Component } from 'react';
//observable:转换为可观察对象
//autorun:监听
import { observable,autorun } from 'mobx';
//普通类型数据的监听
var observableNumber=observable.box(99);
var observableName=observable.box("66球");
//第一次加载时执行,之后每次更新都会执行
//autorun更新到才会执行
autorun(()=>{
console.log(observableNumber.get());
})
autorun(()=>{
console.log(observableName.get());
})
setTimeout(() => {
observableNumber.set(100)
}, 2000);
setTimeout(() => {
observableName.set("溜溜球")
}, 1000);
//对象
var obj=observable.map({
name:"刘德华",
age:18
});
autorun(()=>{
console.log(obj.get('name'));
})
setTimeout(() => {
obj.set('name','华仔')
}, 3000);
setTimeout(() => {
obj.set('age',19)
}, 4000);
class App extends Component {
render() {
return (
<div>
App
</div>
);
}
}
export default App;
2.es7装饰器语法应用-可以处理异步
①.让vscode编辑器支持es7装饰器语法
点击文件-首选项-搜索experimentaPecorators-勾选
②.让react支持es7装饰器语法
安装:npm i @babel/core @babel/plugin-proposal-decorators @babel/preset-env
创建 .babelrc文件
{
"presets": [
"@babel/preset-env"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}
创建config-overrides.js
const path = require('path')
const { override, addDecoratorsLegacy } = require('customize-cra')
function resolve(dir) {
return path.join(__dirname, dir)
}
const customize = () => (config, env) => {
config.resolve.alias['@'] = resolve('src')
if (env === 'production') {
config.externals = {
'react': 'React',
'react-dom': 'ReactDOM'
}
}
return config
};
module.exports = override(addDecoratorsLegacy(), customize())
安装依赖
npm i customize-cra react-app-rewired
修改package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
注意:改完配置文件之后要重启服务器: npm start
3.案例
①.store.js
runInAction :解决异步问题
// observable:转换为可观察对象
// autorun:监听
//runInAction:处理异步
import {observable, configure, action,runInAction} from 'mobx';
import axios from 'axios';
// 严格模式, 必须写action,
// 如果是never,可以不写action,
// 最好设置always, 防止任意地方修改值, 降低不确定性。
configure({enforceActions: "always"})
class Store{
@observable isTabbarShow=true;
@observable list=[];
@observable cityName="深圳";
@action changeShow(){
this.isTabbarShow = true
}
@action changeHide(){
this.isTabbarShow = false
}
//异步
@action getCinemaList(){
axios({
url:"https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159",
method:"get",
headers:{
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',
'X-Host': 'mall.film-ticket.cinema.list'
}
}).then(res=>{
// runInAction 解决异步问题
runInAction(()=>{
this.list=res.data.data.cinemas
})
})
}
}
export default new Store();
②.isTabbarShow应用
app.js中显示isTabbarShow
import React, { Component } from 'react';
import Mrouter from "./router/indexRouter";
import Tabbar from "./component/Tabbar";
import "./App.css";
import {autorun} from "mobx";
import store from "./mobx/store";
export default class App extends Component {
state={
isShow:false
}
componentDidMount() {
autorun(()=>{
console.log(store.isTabbarShow);
this.setState({
isShow:store.isTabbarShow
})
})
}
render() {
return (
<div>
<Mrouter>
{this.state.isShow && <Tabbar></Tabbar>}
</Mrouter>
</div>
)
}
}
组件中调用方法changeShow和changeHide控制isTabbarShow
import React,{useEffect} from 'react';
import store from '../mobx/store';
export default function Detail(props) {
useEffect(() => {
console.log("create创建");
store.changeHide()
return () => {
store.changeShow()
console.log("cleanup销毁");
};
}, []);
return (
<div>Detail</div>
)
}
③.应用
unsubscribe():自定义方法取消订阅,需要手动取消订阅
import React, { useState, useEffect } from "react";
import store from "../mobx/store";
import {autorun} from 'mobx';
export default function Cinemas(props) {
const [cinemaList, setcinemaList] = useState([]);
useEffect(() => {
if(store.list.length === 0){
store.getCinemaList()
}
var unsubscribe=autorun(()=>{
console.log(store.list);
setcinemaList(store.list)
})
return () => {
//取消订阅
unsubscribe()
};
}, []);
return (
<div>
<div>
{cinemaList.map((item) => (
<dl key={item.cinemaId} style={{padding:"10px"}}>
<dt>{item.name}</dt>
<dd style={{fontSize:'12px',color:"gray"}}>{item.address}</dd>
</dl>
))}
</div>
cinemaList
</div>
);
}
五、mobx-react
1.安装:npm install mobx-react@7
(1)react 组件里使用 @observer
observer 函数/装饰器可以用来将 React 组件转变成响应式组件。
(2) 可观察的局部组件状态
@observable 装饰器在React组件上引入可观察属性。而不需要通过 React 的冗长和强制性的 setState 机制来管理。
(3)Provider 组件
它使用了 React 的上下文(context)机制,可以用来向下传递 stores。 要连接到这些 stores,需要传递一个 stores名称的列表给 inject,这使得 stores 可以作为组件的 props 使用。this.props
2.案例
①.store.js
// observable:转换为可观察对象
// autorun:监听
//runInAction:处理异步
import {observable, configure, action,runInAction} from 'mobx';
import axios from 'axios';
// 严格模式, 必须写action,
// 如果是never,可以不写action,
// 最好设置always, 防止任意地方修改值, 降低不确定性。
configure({enforceActions: "always"})
class Store{
@observable isTabbarShow=true;
@observable list=[];
@observable cityName="深圳";
@action changeShow(){
this.isTabbarShow = true
}
@action changeHide(){
this.isTabbarShow = false
}
//异步
@action getCinemaList(){
axios({
url:"https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159",
method:"get",
headers:{
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',
'X-Host': 'mall.film-ticket.cinema.list'
}
}).then(res=>{
// runInAction 解决异步问题
runInAction(()=>{
this.list=res.data.data.cinemas
})
})
}
}
export default new Store();
②.主入口文件index.js
import App from "./10-mobx/04-router/App";
import {Provider} from "mobx-react";
import store from "./10-mobx/04-router/mobx/store";
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(
<div className="box">
<Provider store={store}>
<App/>
</Provider>
</div>
);
③.app.js
import React, { Component } from 'react';
import Mrouter from "./router/indexRouter";
import Tabbar from "./component/Tabbar";
import "./App.css";
import {observer,inject} from "mobx-react";
//mobx-react利用高阶组件构建一个父组件
@inject("store")//注入store
@observer
export default class App extends Component {
componentDidMount() {
console.log(this.props.store);//所有store值
}
render() {
return (
<div>
<Mrouter>
{this.props.store.isTabbarShow && <Tabbar></Tabbar>}
</Mrouter>
</div>
)
}
}
④.子页面 .此组件是函数式组件,不用装饰器的写法,用标签的写法<Observer></Observer>,无状态,无需设置取消订阅,会自动取消订阅
import React, { useState, useEffect } from "react";
import store from "../mobx/store";
import {autorun} from 'mobx';
import {Observer} from 'mobx-react';
export default function Cinemas(props) {
useEffect(() => {
if(store.list.length === 0){
store.getCinemaList()
}
return () => {
//取消订阅
};
}, []);
return (
<div>
<Observer>
{
()=>{
return <div>
{store.list.map((item) => (
<dl key={item.cinemaId} style={{padding:"10px"}}>
<dt>{item.name}</dt>
<dd style={{fontSize:'12px',color:"gray"}}>{item.address}</dd>
</dl>
))}
</div>
}
}
</Observer>
cinemaList
</div>
);
}