源码地址:https://github.com/qifutian/learngit/tree/main/mobx/mobx-guide
简单, 可扩展的状态管理库
MobX 是由 Mendix,Coinbase,Facebook 开源和众多个人赞助商所赞助的
React 和 MobX 是一对强力组合,React 负责渲染应用的状态,MobX 负责管理应用状态供 React 使用
浏览器支持
MobX 5 版本运行在任何支持 ES6 proxy 的浏览器,不支持 IE11,Node.js 6
MobX 4 可以运行在任何支持 ES5 的浏览器上
MobX 4 和 5的 API 是相同的
方法一:启用装饰器语法支持
安装babel插件
解决 vscode 编辑器关于装饰器语法的警告
修改配置:"javascript.implicitProjectConfig.experimentalDecorators": true
// 1. 创建store对象 存储默认状态0
// 2. 将store对象放在一个全局的 组件可以够的到的地方
// 3. 让组件获取store对象中的状态 并将状态显示在组件中
class CounterStore {
count = 0;
}
const counter = new CounterStore();
export default counter;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {
Provider } from 'mobx-react';
import App from './App';
import counter from './stores/counterStore';
ReactDOM.render(
<Provider counter={
counter}><App /></Provider>,
document.getElementById('root')
);
mobx修改
// 1. 创建store对象 存储默认状态0
// 2. 将store对象放在一个全局的 组件可以够的到的地方
// 3. 让组件获取store对象中的状态 并将状态显示在组件中
import {
observable, configure, action, runInAction, flow, computed, autorun } from 'mobx';
// 通过配置强制程序使用action函数更改应用程序中的状态
configure({
enforceActions: 'observed'});
class CounterStore {
@observable count = 0;
@action increment () {
this.count = this.count + 1;
}
@action decrement () {
this.count = this.count - 1;
}
}
const counter = new CounterStore();
export default counter;
修改类中普通函数的this指向
// 1. 创建store对象 存储默认状态0
// 2. 将store对象放在一个全局的 组件可以够的到的地方
// 3. 让组件获取store对象中的状态 并将状态显示在组件中
import {
observable, configure, action, runInAction, flow, computed, autorun } from 'mobx';
// 通过配置强制程序使用action函数更改应用程序中的状态
configure({
enforceActions: 'observed'});
class CounterStore {
@observable count = 0;
@action.bound increment () {
this.count = this.count + 1;
}
@action.bound decrement () {
this.count = this.count - 1;
}
}
const counter = new CounterStore();
export default counter;
异步更新组件方式
引入axios
@observable count = 0;
@observable users = [];
@action.bound increment () {
this.count = this.count + 1;
}
@action.bound decrement () {
this.count = this.count - 1;
}
@action.bound getData = flow(function* () {
let {
data } = yield axios.get('https://api.github.com/users');
this.users = data
}).bind(this)
调用时候报错,需要用一个方法包裹,引入runInAction
// @action.bound async getData () {
// let { data } = await axios.get('https://api.github.com/users');
// runInAction(() => this.users = data);
// }
或使用flow方法
// getData = flow(function* () {
// let { data } = yield axios.get('https://api.github.com/users');
// this.users = data
// }).bind(this)
computed计算值
计算值是可以根据现有的状态或其它计算值衍生出的值
在复杂组件使用,将复杂的业务逻辑从模板中进行抽离
@computed get getResult () {
return this.count * 10
}
在视图通过action函数修改程序状态,当状态发生更改之后,在将状态同步给视图
当普通函数修改状态时候,不好监测
应该只有action函数能修改
通过配置方式,configure方法
// 通过配置强制程序使用action函数更改应用程序中的状态
configure({
enforceActions: 'observed'});
当监测的状态发生变化时,你想根据状态产生 “效果”,请使用 autorun。
autorun 会在初始化的时候执行一次,会在每次状态发生变化时执行。
// 1. 创建store对象 存储默认状态0
// 2. 将store对象放在一个全局的 组件可以够的到的地方
// 3. 让组件获取store对象中的状态 并将状态显示在组件中
import {
observable, configure, action, runInAction, flow, computed, autorun } from 'mobx';
import axios from 'axios';
// 通过配置强制程序使用action函数更改应用程序中的状态
configure({
enforceActions: 'observed'});
class CounterStore {
constructor () {
autorun (() => {
try {
uniqueUsername(this.username)
console.log('用户名可用')
}catch (e) {
console.log(e.message)
}
}, {
delay: 2000
})
}
@observable count = 0;
@observable users = [];
@observable username = '';
// @action increment = () => {
// this.count = this.count + 1;
// }
// @action decrement = () => {
// this.count = this.count - 1;
// }
@action.bound increment () {
this.count = this.count + 1;
}
@action.bound decrement () {
this.count = this.count - 1;
}
// @action.bound async getData () {
// let { data } = await axios.get('https://api.github.com/users');
// runInAction(() => this.users = data);
// }
// getData = flow(function* () {
// let { data } = yield axios.get('https://api.github.com/users');
// this.users = data
// }).bind(this)
@computed get getResult () {
return this.count * 10
}
@action.bound changeUsername (username) {
this.username = username;
}
}
const counter = new CounterStore();
function uniqueUsername (username) {
return new Promise((resolve, reject) => {
if (username === 'admin') {
reject('用户名已经存在')
}else {
resolve()
}
})
}
export default counter;
App.js
import React, {
Component } from "react";
import {
inject, observer } from "mobx-react";
@inject("counter")
@observer
class App extends Component {
componentDidMount() {
// const { getData } = this.props.counter;
// getData();
}
render() {
const {
counter } = this.props;
return (
<div>
<button onClick={
counter.increment}>+</button>
<span>{
counter.count}</span>
<button onClick={
counter.decrement}>-</button>
<span>{
counter.getResult}</span>
<div>
<input type="text" value={
counter.username} onChange={
e => counter.changeUsername(e.target.value)}/>
</div>
{
/*
{counter.users.map((user) => (
{user.id}
{user.login}
))}
*/}
</div>
);
}
}
export default App;
Todo案例构建项目组件
实现一个对任务的增删改查任务和筛选等
src下的store放mobx代码,component放任务相关代码
App.js
import React, {
Component } from 'react';
import AddTodo from './addTodo';
import TodoList from './todoList';
import TodoExtra from './todoExtra';
class App extends Component {
render() {
return <section className="todoapp">
<AddTodo />
<TodoList />
<TodoExtra />
</section>
}
}
export default App;
addTodo.js
import React, {
Component } from "react";
import {
inject, observer } from "mobx-react";
@inject('todo')
@observer
class AddTodo extends Component {
// 添加任务
addTodo (event) {
const {
todoAdd } = this.props.todo;
// 判断用户敲击的是否是回车键
if (event.key === 'Enter') {
// 获取用户在文本框中输入的内容
const taskName = event.target.value;
// 判断用户在文本框中是否输入了内容
if (taskName.trim().length === 0) {
// 阻止程序向下执行
return;
}
// 将任务添加到任务列表数组中
todoAdd (taskName);
// 清空文本框中的内容
event.target.value = '';
}
}
render() {
return (
<header className="header">
<h1>todos</h1>
<input onKeyUp={
this.addTodo.bind(this)} className="new-todo" placeholder="What needs to be done?" />
</header>
);
}
}
export default AddTodo;
todoExtra.js
import React, {
Component } from "react";
import {
inject, observer } from "mobx-react";
@inject("todo")
@observer
class TodoExtra extends Component {
render() {
const {
unfinishedTodoCount, changeFilter, filter } = this.props.todo;
return (
<footer className="footer">
<span className="todo-count">
<strong>{
unfinishedTodoCount}</strong> item left
</span>
<ul className="filters">
<li>
<button className={
filter === 'All' ? 'selected': ''} onClick={
() => changeFilter('All')}>All</button>
</li>
<li>
<button className={
filter === 'Active' ? 'selected': ''} onClick={
() => changeFilter('Active')}>Active</button>
</li>
<li>
<button className={
filter === 'Completed' ? 'selected': ''} onClick={
() => changeFilter('Completed')}>Completed</button>
</li>
</ul>
<button className="clear-completed">Clear completed</button>
</footer>
);
}
}
export default TodoExtra;
todoList.js
import React, {
Component } from "react";
import {
inject, observer } from "mobx-react";
@inject("todo")
@observer
class TodoList extends Component {
render() {
const {
todoDelete, changeCompleted, filterTodo } = this.props.todo;
return (
<section className="main">
<input className="toggle-all" type="checkbox" />
<ul className="todo-list">
{
filterTodo.map((todo, index) => {
return (
<li className={
todo.isCompleted ? 'completed': ''} key={
todo.taskName}>
<div className="view">
<input onChange={
event => changeCompleted(index, event.target.checked)} checked={
todo.isCompleted} className="toggle" type="checkbox" />
<label>{
todo.taskName}</label>
<button onClick={
() => todoDelete(index)} className="destroy"></button>
</div>
<input className="edit" />
</li>
);
})}
</ul>
</section>
);
}
}
export default TodoList;
TodoStore.js
import {
observable, action, computed } from 'mobx';
class TodoStore {
// todo 列表
@observable todos = [];
// 筛选条件
@observable filter = 'All';
// 添加任务
@action.bound todoAdd (taskName) {
this.todos.push({
taskName,
isCompleted: false
})
}
// 删除任务
@action.bound todoDelete (index) {
this.todos.splice(index, 1);
}
// 更改任务的是否已完成状态
@action.bound changeCompleted (index, flag) {
this.todos[index].isCompleted = flag;
}
@computed get unfinishedTodoCount () {
return this.todos.filter(todo => todo.isCompleted === false).length;
}
// 更改筛选条件
@action.bound changeFilter (condition) {
this.filter = condition;
}
// 监测筛选条件的变化 根据筛选条件对任务进行筛选
@computed get filterTodo () {
switch (this.filter) {
case 'All':
return this.todos;
case 'Active':
return this.todos.filter(todo => todo.isCompleted === false);
case 'Completed':
return this.todos.filter(todo => todo.isCompleted === true);
}
}
}
const todo = new TodoStore();
export default todo;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import todo from './stores/TodoStore';
import {
Provider } from 'mobx-react';
import './index.css';
ReactDOM.render(
<Provider todo={
todo}><App /></Provider>,
document.getElementById('root')
);