<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • React MobX 開始

    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 個人實踐的經驗分享,可關注公眾號!

    posted @ 2021-12-28 19:20  GoCodingInMyWay  閱讀(161)  評論(0編輯  收藏  舉報
    国产美女a做受大片观看