MobX 用于状态管理,简单高效。本文将于 React 上介绍如何开始,包括了:

  • 了解 MobX 概念
  • 从零准备 React 应用
  • MobX React.FC 写法
  • MobX React.Component 写法

可以在线体验: https://ikuokuo.github.io/start-react ,代码见: https://github.com/ikuokuo/start-react

概念

首先,ui 是由 state 通过 fn 生成:

ui = fn(state)

在 React 里, fn 即组件,依照自己的 state 渲染。

如果 state 是共享的,一处状态更新,多处组件响应呢?这时就可以用 MobX 了。

MobX 数据流向如下:

      ui

action → state

ui 触发 action,更新 state,重绘 ui。注意是单向的。

了解更多,请阅读 MobX 主旨 。这里讲下实现时的主要步骤:

  • 定义数据存储类 Data Store

    • 成员属性为 state,成员函数为 action
    • mobx 标记为 observable
  • 定义 Stores Provider
    • 方式一 React.ContextcreateContext 包装 Store 实例,ui useContext 使用
    • 方式二 mobx-react.Provider:直接包装 Store 实例,提供给 Providerui inject 使用
  • 实现 ui 组件
    • mobx 标记为 observer
    • 获取 stores,直接引用 state
    • 若要更新 state,间接调用 action

项目结构上就是多个 stores 目录,定义各类 storestate action,异步操作也很简单。了解更多,请阅读:

准备

React App

yarn create react-app start-react --template typescript
cd start-react

React Router

路由库,以便导航样例。

yarn add react-router-dom

Antd

组件库,以便布局 UI。

yarn add antd @ant-design/icons

高级配置

yarn add @craco/craco -D
yarn add craco-less

craco.config.js 配置了深色主题:

const path = require('path');
const CracoLessPlugin = require('craco-less');
const { getThemeVariables } = require('antd/dist/theme'); module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: getThemeVariables({
dark: true,
// compact: true,
}),
javascriptEnabled: true,
},
},
},
},
],
webpack: {
alias: { '@': path.resolve(__dirname, './src') },
},
};

ESLint

VSCode 安装 ESLint Prettier 扩展。初始化 eslint

$ npx eslint --init
How would you like to use ESLint? · style
What type of modules does your project use? · esm
Which framework does your project use? · react
Does your project use TypeScript? · No / Yes
Where does your code run? · browser
How would you like to define a style for your project? · guide
Which style guide do you want to follow? · airbnb
What format do you want your config file to be in? · JavaScript

配置 .eslintrc.js .eslintignore .vscode/settings.json,详见代码。并于 package.json 添加:

"scripts": {
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern node_modules/"
},

执行 yarn lint 通过, yarn start 运行。

到此, React Antd 应用就准备好了。初始模板如下,可见首个提交:

MobX

yarn add mobx mobx-react

mobx-react 包含了 mobx-react-lite,所以不必安装了。

  • 如果只用 React.FC (HOOK) 时,用 mobx-react-lite 即可。
  • 如果要用 React.Component (Class) 时,用 mobx-react 才行。

mobx-react-lite 与 React.FC

定义 Data Stores

makeAutoObservable

定义数据存储模型后,于构造函数里调用 makeAutoObservable(this) 即可。

stores/Counter.ts:

import { makeAutoObservable } from 'mobx';

class Counter {
count = 0; constructor() {
makeAutoObservable(this);
} increase() {
this.count += 1;
} decrease() {
this.count -= 1;
}
} export default Counter;

React.Context Stores

React.Context 可以很简单的传递 Stores

stores/index.ts:

import React from 'react';

import Counter from './Counter';
import Themes from './Themes'; const stores = React.createContext({
counter: new Counter(),
themes: new Themes(),
}); export default stores;

创建一个 useStoresHook,简化调用。

hooks/useStores.ts:

import React from 'react';
import stores from '../stores'; const useStores = () => React.useContext(stores); export default useStores;

Pane 组件,使用 Stores

组件用 observer 包装,useStores 引用 stores

Pane.tsx:

import React from 'react';
import { Row, Col, Button, Select } from 'antd';
import { PlusOutlined, MinusOutlined } from '@ant-design/icons';
import { observer } from 'mobx-react-lite'; import useStores from './hooks/useStores'; type PaneProps = React.HTMLProps<HTMLDivElement> & {
name?: string;
} const Pane: React.FC<PaneProps> = ({ name, ...props }) => {
const stores = useStores(); return (
<div {...props}>
{name && <h2>{name}</h2>}
<Row align="middle">
<Col span="4">Count</Col>
<Col span="4">{stores.counter.count}</Col>
<Col>
<Button
type="text"
icon={<PlusOutlined />}
onClick={() => stores.counter.increase()}
/>
<Button
type="text"
icon={<MinusOutlined />}
onClick={() => stores.counter.decrease()}
/>
</Col>
</Row>
{/* ... */}
</div>
);
}; Pane.defaultProps = { name: undefined }; export default observer(Pane);

mobx-react 与 React.Component

定义 Data Stores

makeObservable + decorators

装饰器在 MobX 6 中放弃了,但还可使用。

首先,启用装饰器语法TypeScripttsconfig.json 里启用:

"experimentalDecorators": true,
"useDefineForClassFields": true,

定义数据存储模型后,于构造函数里调用 makeObservable(this)。在 MobX 6 前不需要,但现在为了装饰器的兼容性必须调用。

stores/Counter.ts:

import { makeObservable, observable, action } from 'mobx';

class Counter {
@observable count = 0; constructor() {
makeObservable(this);
} @action
increase() {
this.count += 1;
} @action
decrease() {
this.count -= 1;
}
} export default Counter;

Root Stores

组合多个 Stores

stores/index.ts:

import Counter from './Counter';
import Themes from './Themes'; export interface Stores {
counter: Counter;
themes: Themes;
} const stores : Stores = {
counter: new Counter(),
themes: new Themes(),
}; export default stores;

父组件,提供 Stores

父组件添加 mobx-react.Provider,并且属性扩展 stores

index.tsx:

import React from 'react';
import { Provider } from 'mobx-react';
import stores from './stores'; import Pane from './Pane'; const MobXCLS: React.FC = () => (
<div>
<Provider {...stores}>
<h1>MobX with React.Component</h1>
<div style={{ display: 'flex' }}>
<Pane name="Pane 1" style={{ flex: 'auto' }} />
<Pane name="Pane 2" style={{ flex: 'auto' }} />
</div>
</Provider>
</div>
); export default MobXCLS;

Pane 组件,注入 Stores

组件用 observer 装饰,同时 inject 注入 stores

Pane.tsx:

import React from 'react';
import { Row, Col, Button, Select } from 'antd';
import { PlusOutlined, MinusOutlined } from '@ant-design/icons';
import { observer, inject } from 'mobx-react'; import { Stores } from './stores'; type PaneProps = React.HTMLProps<HTMLDivElement> & {
name?: string;
}; @inject('counter', 'themes')
@observer
class Pane extends React.Component<PaneProps, unknown> {
get injected() {
return this.props as (PaneProps & Stores);
} render() {
const { name, ...props } = this.props;
const { counter, themes } = this.injected; return (
<div {...props}>
{name && <h2>{name}</h2>}
<Row align="middle">
<Col span="4">Count</Col>
<Col span="4">{counter.count}</Col>
<Col>
<Button
type="text"
icon={<PlusOutlined />}
onClick={() => counter.increase()}
/>
<Button
type="text"
icon={<MinusOutlined />}
onClick={() => counter.decrease()}
/>
</Col>
</Row>
<Row align="middle">
<Col span="4">Theme</Col>
<Col span="4">{themes.currentTheme}</Col>
<Col>
<Select
style={{ width: '60px' }}
value={themes.currentTheme}
showArrow={false}
onSelect={(v) => themes.setTheme(v)}
>
{themes.themes.map((t) => (
<Select.Option key={t} value={t}>
{t}
</Select.Option>
))}
</Select>
</Col>
</Row>
</div>
);
}
} export default Pane;

最后

MobX 文档可以浏览一遍,了解有哪些内容。未涉及的核心概念还有 Computeds, Reactions

其中 MobX and React 一节,详解了于 React 中的用法及注意点,见:React 集成React 优化

GoCoding 个人实践的经验分享,可关注公众号!

React MobX 开始的更多相关文章

  1. [Web 前端] 如何构建React+Mobx+Superagent的完整框架

    ReactJS并不像angular一样是一个完整的前端框架,严格的说它只是一个UI框架,负责UI页面的展示,如果用通用的框架MVC来说,ReactJs只负责View了,而Angular则是一个完整的前 ...

  2. 前端003/【React + Mobx + NornJ】开发模式

    1.React + Mobx + NornJ 开发模式快速上手教程 github网址:https://github.com/joe-sky/nornj-cli/blob/master/docs/gui ...

  3. [React + Mobx] Mobx and React intro: syncing the UI with the app state using observable and observer

    Applications are driven by state. Many things, like the user interface, should always be consistent ...

  4. react mobx 装饰器语法配置

    1.弹出项目配置 npm run eject 此处注意,若弹出项目配置失败,请先执行以下两行代码(我的项目执行上一句都会报错,所以都会执行) 1.git add . 2.git commit -m & ...

  5. react mobx webpack 使用案例

    1.package.json: { "name": "wtest", "version": "1.0.0", " ...

  6. React + MobX 状态管理入门及实例

    前言 现在最热门的前端框架,毫无疑问是React. React是一个状态机,由开始的初始状态,通过与用户的互动,导致状态变化,从而重新渲染UI. 对于小型应用,引入状态管理库是"奢侈的&qu ...

  7. react+mobx 编写 Async装饰器

    使用 // indexStore.js import axios from "axios"; import { from } from "rxjs"; impo ...

  8. react+mobx 编写 withStoreHistory 装饰器

    主要作用是向store里面注入一个history对象,方便story里面的函数调用 function withStoreHistory(storeName) { if (!storeName) ret ...

  9. Vue.js 2.0 和 React、Augular等其他框架的全方位对比

    引言 这个页面无疑是最难编写的,但也是非常重要的.或许你遇到了一些问题并且先前用其他的框架解决了.来这里的目的是看看Vue是否有更好的解决方案.那么你就来对了. 客观来说,作为核心团队成员,显然我们会 ...

随机推荐

  1. POLLOUT/POLLINT事件触发测试

    一般poll使用过程中都是检测POLLIN事件,表示描述符有事件可以处理了,需要我们处理.对POLLOUT事件触发的方式相对较少,网上也有很多对此触发的疑问,利用实际项目中用的一个用法,下面做了个测试 ...

  2. 运算符重载+日期类Date

    Hello,一只爱学习的鱼 大学学习C++运算符重载的时候,老师出了一道"运算符重载+类"的综合练习题,让我们来一起看看吧! 题目: 设计一个日期类Date,包括年.月.日等私有成 ...

  3. A Child's History of England.31

    The English in general were on King Henry's side, though many of the Normans were on Robert's. But t ...

  4. day09 文件属性

    day09 文件属性 昨日回顾 yum底层原理: 第一步:执行yum install nginx安装命令 第二步:yum去/etc/yum.repos.d这个目录中 第三步:根据/etc/yum/re ...

  5. 零基础学习java------25--------jdbc

    jdbc开发步骤图 以下要用到的products表 一. JDBC简介 补充    JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口,各个数据库厂商趋势线这个接口,提 ...

  6. JavaIO——转换流、字符编码

    1.转换流 转换流是将字节流变成字符流的流. OutputStreamWriter:将字节输出流转换成字符输出流. public class OutputStreamWriter extends Wr ...

  7. Java Spring 自定义事件监听

    ApplicationContext 事件 定义一个context的起动监听事件 import org.springframework.context.ApplicationListener; imp ...

  8. 动态规划系列(零)—— 动态规划(Dynamic Programming)总结

    动态规划三要素:重叠⼦问题.最优⼦结构.状态转移⽅程. 动态规划的三个需要明确的点就是「状态」「选择」和「base case」,对应着回溯算法中走过的「路径」,当前的「选择列表」和「结束条件」. 某种 ...

  9. jstl中的if标签

    <%@ page import="java.util.ArrayList" %><%@ page import="java.util.List" ...

  10. Python绘制折线图

    一.Python绘制折线图 1.1.Python绘制折线图对应代码如下图所示 import matplotlib.pyplot as pltimport numpy as np from pylab ...