MobX 用于状态治理,简略高效。本文将于 React 上介绍如何开始,包含了:
- 理解 MobX 概念
- 从零筹备 React 利用
- MobX React.FC 写法
- MobX React.Component 写法
能够在线体验: https://ikuokuo.github.io/sta... ,代码见: https://github.com/ikuokuo/st... 。
概念
首先,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
,异步操作也很简略。理解更多,请浏览:
- 定义数据存储
- 异步 actions
筹备
React App
yarn create react-app start-react --template typescriptcd start-react
React Router
路由库,以便导航样例。
yarn add react-router-dom
Antd
组件库,以便布局 UI。
yarn add antd @ant-design/icons
高级配置,
yarn add @craco/craco -Dyarn 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')@observerclass 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 集体实际的教训分享,可关注公众号!