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.Context:createContext 包装 Store 实例,ui useContext 使用
    • 方式二 mobx-react.Provider:直接包装 Store 实例,提供给 Provider,ui inject 使用
  • 实现 ui 组件
    • 用 mobx 标记为 observer
    • 获取 stores,直接引用 state
    • 若要更新 state,间接调用 action

项目结构上就是多个 stores 目录,定义各类 store 的 state 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;

创建一个 useStores 的 Hook,简化调用。

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 中放弃了,但还可使用。

首先,启用装饰器语法。TypeScript 于 tsconfig.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. Vue3 中有哪些值得深究的知识点?

    众所周知,前端技术一直更新很快,这不 vue3 也问世这么久了,今天就来给大家分享下vue3中值得注意的知识点.喜欢的话建议收藏,点个关注! 1.createApp vue2 和 vue3 在创建实例 ...

  2. 『学了就忘』Linux文件系统管理 — 67、通过命令模式进行LVM分区

    目录 1.物理卷管理 (1)准备硬盘或者分区 (2)建立物理卷 (3)查看物理卷 (3)删除物理卷 2.创建卷组 (1)建立卷组 (2)查看卷组 (3)增加卷组容量 (4)减小卷组容量 (5)删除卷组 ...

  3. java9 模块化 jigsaw

    java9并没有在语言层面做出很多改变,而是致力于一些新特性,如模块化,其核心就是解决历史遗留问题,为以后的jar包森林理清道路.模块化是一个很大的命题,就不讲那么细致了,关于java9的特性也有很多 ...

  4. Spark基础:(五)Spark编程进阶

    共享变量 (1)累加器:是用来对信息进行聚合的,同时也是Spark中提供的一种分布式的变量机制,其原理类似于mapreduce,即分布式的改变,然后聚合这些改变.累加器的一个常见用途是在调试时对作业执 ...

  5. ES5中改变this指向的三种方法

    ES5中提供了三种改变函数中this指针指向的方法,分别如下 1.call() var obj = {username:"孙悟空"}; //没有任何修饰的调用函数,函数中的this ...

  6. keil 生成 bin 文件 gd32为例

    fromelf --bin --output .\update\GD32F4xZ.bin .\Output\GD32450Z_EVAL.axf代表使用的keil内的工具代表输出公式,..表示: 输出 ...

  7. Android权限级别(protectionLevel)

    通常情况下,对于需要付费的操作以及可能涉及到用户隐私的操作,我们都会格外敏感. 出于上述考虑以及更多的安全考虑,Android中对一些访问进行了限制,如网络访问(需付费)以及获取联系人(涉及隐私)等. ...

  8. C++ 之杂记

    今天做了一个题,代码不难,但是编译的时候就恼火,老是报错,也不告诉我错哪了.... 之前的代码是这样的,在main函数中调用这个类的构造函数,就一直报错,但是不知道原因,后来加上了const 就好了. ...

  9. oracle to_char处理日期

    select to_char(sysdate,'d') from dual;--本周第几天 select to_char(sysdate,'dd') from dual;--本月第几天 select ...

  10. Can we use function on left side of an expression in C and C++?

    In C, it might not be possible to have function names on left side of an expression, but it's possib ...