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. Spring DAO

    Spring DAO 连接池 使用JDBC访问数据库是,频繁的打开连接和关闭连接,造成性能影响,所以有了连接池.数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接, ...

  2. apostrophe

    apostrophe 者,', 0x27, 十进制39,ASCII里的single quote (单引号) 也.one of the 'inverted commas'. 在书写上可以表示所有格.省略 ...

  3. day03 MySQL数据库之主键与外键

    day03 MySQL数据库之主键与外键 昨日内容回顾 针对库的基本SQL语句 # 增 create database meng; # 查 show databases; shwo create da ...

  4. InnoDB学习(二)之ChangeBuffer

    ChangeBuffer是InnoDB缓存区的一种特殊的数据结构,当用户执行SQL对非唯一索引进行更改时,如果索引对应的数据页不在缓存中时,InnoDB不会直接加载磁盘数据到缓存数据页中,而是缓存对这 ...

  5. 零基础学习java------34---------登录案例,域,jsp(不太懂),查询商品列表案例(jstl标签)

    一. 简单登录案例 流程图: 项目结构图 前端代码: <!DOCTYPE html> <html> <head> <meta charset="UT ...

  6. 【Netty】最透彻的Netty原理架构解析

    这可能是目前最透彻的Netty原理架构解析 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件.整体架构,知其然且知其所以然,希望给大家在实际开发实践.学习开源项目方面提供参考. ...

  7. java网站架构设计

    涉及到的技术及工具:java,springmvc,ibatis,freemarker,mysql,mongdb,memcached,ehcache,maven. 一个网站不可能说一开始就是要设计一个能 ...

  8. mango后台

     环境搭建 项目配置 下载后导入项目,删除mvnw.mvnw.cmd两个文件 修改spring-boot-starter-web pom.xml --> run as --> mave i ...

  9. SQL Server 和 Oracle 以及 MySQL 数据库

    推荐:https://www.zhihu.com/question/19866767 三者是目前市场占有率最高(依安装量而非收入)的关系数据库,而且很有代表性.排行第四的DB2(属IBM公司),与Or ...

  10. java实现文件压缩

    java实现文件压缩:主要是流与流之间的传递 代码如下: package com.cst.klocwork.service.zip; import java.io.File; import java. ...