发布于 2026-01-06 0 阅读
0

使用 Mobx 和 React 构建你的第一个应用

使用 Mobx 和 React 构建你的第一个应用

除了ReduxContext API之外, MobX是另一个可用于 React 应用的状态管理库。然而,MobX 不仅适用于 React,它也适用于其他用于 Web 应用前端的 JavaScript 库和框架。MobX 5 及更高版本可在任何支持 ES6 代理的浏览器上运行。

主要概念

以下是mobx的主要概念:

可观测的

可观察对象允许我们将任何数据结构或属性转换为可观察状态,以便其他事物可以跟踪这些可观察的变化和值。

行动

该操作允许我们更改可观察对象的状态(即值)。为确保代码的可扩展性,状态数据不应在操作之外被修改。

计算

计算属性允许我们根据状态变化推导出一些值。这些计算值是通过对可观察对象执行某种计算而获得的。

观察者

观察者机制允许我们跟踪可观察对象的变化,以便 React 在任何变化发生时都能收到通知并开始重新渲染。它们由 mobx-react 包提供。

店铺

store 是数据源。与 Redux 不同,在 MobX 中,数据和修改数据的函数都存储在 store 中。因此,store 可以包含 Observable 和 Action。

简而言之,MobX 基于可观察对象-观察者流程工作。你声明一些数据为可观察对象,当这些数据发生变化时,所有使用该数据的观察者都会收到通知。

现在让我们把这些概念付诸实践。

我们将创建一个简单的应用程序,用户可以在上面对图片做出反应并发表评论,类似于Facebook。这是演示链接

项目设置

假设您已具备 React 的相关知识,则需要在您的计算机上安装NPMNode.js。

我正在使用自定义的 webpack 配置,并设置项目以启用装饰器。别担心,还有一种不使用装饰器的替代方法。在这个例子中,我还是使用了装饰器,因为它更简洁。不过我也会提到其他方法。如果您使用的是 create-react-app,则可以跳过这些设置步骤。

master从此仓库拉取分支进行初始设置。

运行yarn以安装依赖项并使用.该应用程序将在http://localhost:8080yarn start上运行。

装饰师的准备工作

要启用 ESNext 装饰器,需要以下插件。

yarn add --dev @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators
Enter fullscreen mode Exit fullscreen mode

然后将以下配置添加到 .babelrc 文件中。

"plugins": [
        [
            "@babel/plugin-proposal-decorators",
            {
                "legacy": true
            }
        ],
        [
            "@babel/plugin-proposal-class-properties",
            {
                "loose": true
            }
        ]
    ]
Enter fullscreen mode Exit fullscreen mode

款式

请拉取设计分支以获取样式。所有样式都位于css目录下的相应文件夹内src。以下是我们的应用组件示意图。

  • 卡片组件包含:
  1. 随机生成的图像。

  2. 计数组件用于跟踪点赞数和评论数。

  3. 包含点赞和评论按钮的按钮组件。

  • 包含用于发表评论的输入字段的表单组件。

  • 评论组件,包含评论列表。

安装依赖项

安装mobx状态管理解决方案和mobx-react库,将状态层连接到 React 视图层。

yarn add mobx mobx-react
Enter fullscreen mode Exit fullscreen mode

现在我们将开始使用 Mobx 添加功能。


店铺

首先,我们要创建一个Store.jsxstore文件夹。

import { observable, action } from 'mobx'

class Store {
    @observable likesCount = 12

    @action updateCount{
        this.likesCount++;
    }
}

const storeInstance = new Store()
export default storeInstance;
Enter fullscreen mode Exit fullscreen mode

在这里,我们创建了一个 Store 类,它有likesCount一个可观察的状态,updateCount一个用于修改状态的操作,然后导出了 Store 的一个新实例。

如果你的配置不支持装饰器,则上述代码可以重写为:

import { decorate, observable } from "mobx";

class Store {
    likesCount = 12;

    updateCount{
        this.likesCount++;
    }
}

decorate(Store, {
    likesCount: observable,
    updateCount: action
})
Enter fullscreen mode Exit fullscreen mode

然后,我们通过使用 Context API 传递该存储,使其在整个应用程序中可访问main.js


import storeInstance from './store/Store'

export const StoreContext = React.createContext();

ReactDOM.render(
        <StoreContext.Provider value={storeInstance}>
            <Post />
        </StoreContext.Provider >
    , document.getElementById('app'));
Enter fullscreen mode Exit fullscreen mode

Count.jsx现在我们可以使用`.` 访问 store 及其类属性useContext。由于我们已将 `.` 的初始值设置likesCount为 12,因此您的应用程序将渲染该值。

import React, { useContext } from 'react';
import { StoreContext } from '../main'

export default function Count() {
    const store = useContext(StoreContext)
    return (
        <div className="row reactions-count" >
            <div className="col-sm" align="left">
                <i className="fa fa-thumbs-up" />{store.likesCount}
            </div>
            <div className="col-sm" align="right">
                3 comments
        </div>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

请记住,可观察状态只能通过操作进行修改。因此,为了likesCount在用户点击“点赞”按钮时递增状态值,我们将使用updateCount已定义的 store 中的操作。onClick在 . 中处理操作Buttons.jsx

const store = useContext(StoreContext)
<button type="button" className="btn btn-light align-top" onClick={() => store.updateCount()}>
  <i className="fa fa-thumbs-o-up" />
  Like
</button>
Enter fullscreen mode Exit fullscreen mode

如果你点击“点赞”按钮,不会看到任何变化。

为了观察并响应函数组件的变化,我们可以将组件包装在观察者函数中,或者实现 useObserver hook,如下所示。因此,让我们进行Count.jsx如下更新:

import { useObserver } from 'mobx-react';

...
  return useObserver(() => (
        <div className="row reactions-count" >
            <div className="col-sm" align="left">
                <i className="fa fa-thumbs-up" />{store.likesCount}
            ...
            ...
        </div>
        </div>
    ))
Enter fullscreen mode Exit fullscreen mode

现在点击按钮后,点赞数会更新。


评论

我们开始整理评论区吧。

数组数据结构也可以是可观察的。让我们创建一个可观察comments字段。在代码中添加以下内容Store.jsx

@observable comments = ["Wow", "awesome"]
Enter fullscreen mode Exit fullscreen mode

Comments.jsx然后像之前一样,通过Count.jsx`using` 属性访问 `Store` 类的 `comments` 属性useContext。现在,`Comments` 组件将从 `store` 中渲染评论。

import React, { useContext } from 'react';
import { StoreContext } from '../main';

export default function Comments() {
    const store = useContext(StoreContext)
    return (
        <table className="table">
            <tbody>
                {
                    store.comments.map((comment, index) => {
                        return (
                            <tr key={index}>
                                <td>
                                    {comment}
                                </td>
                            </tr>
                        )

                    })
                }
            </tbody>
        </table>
    )
}
Enter fullscreen mode Exit fullscreen mode

我们还需要允许用户通过表单添加评论。

首先,让我们postComment在 store 中创建一个名为 `add` 的操作,该操作会将新评论添加到之前的评论数组中。在 `.` 中添加以下代码行Store.jsx

@action postComment(comment){
            this.comments.push(comment)
}
Enter fullscreen mode Exit fullscreen mode

然后按如下方式更新Form.jsx组件:

import React, { useContext } from 'react';
import { StoreContext } from '../main';

export default class Form extends React.Component {

    handleSubmit = (e, store) => {
        e.preventDefault();
        store.postComment(this.comment.value);
        this.comment.value = "";
    }

    render() {
        return (
            <StoreContext.Consumer>
                {
                    store => (

                        <form onSubmit={(e) => this.handleSubmit(e, store)}>
                            <div>
                                <input type="text" id={'comment'} className="form-control" placeholder={"Write a comment ..."} ref={node => {
                                    this.comment = node;
                                }} />
                            </div>
                        </form>
                    )
                }
            </StoreContext.Consumer>
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

postComment在这里,我们只是创建了一个函数,当用户提交评论时调用商店的操作,并在提交后将输入字段设置为空。

为了在添加新评论时更新评论组件,我们需要像对 Count 组件那样,将 Comments 组件设置为观察者。因此,需要Comments.jsx将要返回的内容包装起来useObserver。另外,别忘了导入useObserver

return useObserver(() => (
        <table className="table">
            <tbody>
                {
                    store.comments.map((comment, index) => {
                       ...
                       ...
                }
            </tbody>
        </table>
    )
    )
Enter fullscreen mode Exit fullscreen mode

现在,如果您输入任何评论并按下回车键,您的评论列表将自动更新。

让我们在点击评论按钮时聚焦到输入框。我们可以直接使用 HTML DOM 的 focus() 方法。但首先,让我们给输入框添加一个 id。

<input type="text" id={'comment'} className="form-control" placeholder={"Write a comment ..."} 
ref={node => {this.comment = node;}} />
Enter fullscreen mode Exit fullscreen mode

onClick然后,在组件的评论按钮处理程序中添加 focus 方法Buttons.jsx

<button type="button" className="btn btn-light" 
onClick={() => document.getElementById('comment').focus()}>
  <i className="fa fa-comment-o" />
  Comment
</button>
Enter fullscreen mode Exit fullscreen mode

现在,当您点击评论按钮时,评论字段将获得焦点。

计算

为了获取评论数量,我们将创建一个commentsCountgetter 函数来计算可观察comments数组的长度。MobX 会在数组发生变化commentsCount时自动更新comments。请Store.jsx添加以下代码:

@computed get commentsCount(){
            return this.comments.length;
}
Enter fullscreen mode Exit fullscreen mode

然后只需更新以下几行即可Count.jsx

<div className="col-sm" align="right">
      {store.commentsCount} comments
</div>
Enter fullscreen mode Exit fullscreen mode

你还会注意到,当你添加评论时,计数也会随之更新。


服务/API调用

在应用程序中,调用 API 和编写异步代码非常常见。由于这是一个自定义的 webpack 配置,要启用 async/await,请更新文件.babelrc如下。

"presets": [
        ["@babel/preset-env",
        {
            "targets": {
              "node": "10"
            }
          }
        ],
        "@babel/preset-react"
    ],
Enter fullscreen mode Exit fullscreen mode

否则你可能会遇到这个错误

让我们在按钮点击时更改组件中的图像Card.jsx。我们将使用这个有趣且免费的 API 来获取《瑞克和莫蒂》动画剧集中的角色图像。更多详情请查看他们的文档。

从本节中你会发现,我们可以通过添加id参数来获取单个字符:/character/1

https://rickandmortyapi.com/api/character/1
Enter fullscreen mode Exit fullscreen mode

我们创建一个imageUrl包含默认值的可观察对象的图像存储。然后,我们创建一个fetchImage操作,该操作返回一个包含单个字符的 JSON 响应。

await每次启动新的异步函数await,修改状态的代码都应该被封装成一个 action。有多种方法可以实现这一点。请阅读Mobx 文档的相应章节了解更多详情。

一种方法是使用 `await` runInAction,这是一个简单的实用程序,它接受一个代码块并在匿名操作中执行。这里我们将 `await` 之后的状态修改部分包装在 `await` 中runInAction

import { action, runInAction, observable } from "mobx";

class ImageStore {

    id = 1

    @observable imageUrl = `https://rickandmortyapi.com/api/character/avatar/1.jpeg`

    @action async fetchImage() {
            const characterId = ++this.id
            const response = await fetch(`https://rickandmortyapi.com/api/character/${characterId}`)
            const data = await response.json()
            runInAction(() => {
                this.imageUrl = data.image
            })
    }
}

const imageStore = new ImageStore()

export default imageStore;
Enter fullscreen mode Exit fullscreen mode

你也可以只运行回调函数中修改状态的部分。这里我们创建了一个操作来设置外部 URL fetchImage,然后根据需要调用了它。

class ImageStore {

    ... 

    @action async fetchImage() {
            ...
            this.setImageUrl(data.image)
    }


    @action setImageUrl(url) {
        this.imageUrl = url
    }
}
Enter fullscreen mode Exit fullscreen mode

然后是Card.jsx组件

  • 导入imageStore并将其图像源设置为imageUrlstore 中的 observable。

  • 实施useObserver应对变化的措施。

  • 添加一个带有处理程序的按钮onClick,该处理程序调用fetchImage以获取图像 URL。

import React from "react";
import Count from "./Count";
import Buttons from "./Buttons";
import imageStore from '../store/ImageStore'
import { useObserver } from "mobx-react";

export default function Card() {
    return (
        useObserver(() => (
            <div className="card">
                <img src={imageStore.imageUrl} className="card-img-top" alt="..." />
                <button className="btn btn-light" onClick={() => { imageStore.fetchImage() }}>
                    <i className="fa fa-chevron-right" />
                </button>
                <Count />
                <div className="card-body" >
                    <Buttons />
                </div>
            </div>
        ))
    );
}
Enter fullscreen mode Exit fullscreen mode

好啦,大功告成!最终输出效果如下:


#笔记

将 action 与 store 打包的好处在于,我们可以在onClickhandler 中使用它们。这意味着大多数组件(例如本例中的组件)都可以是无状态函数组件。要使类组件成为观察者,我们可以使用@observer装饰器或将组件包装在observer函数中。

    import React from "react";
    import { observer } from "mobx-react";

    //With decorator
    @observer
    export default class Form extends React.Component{
     ...
    }

    //Without decorator

    class Form extends React.Component{
     ...
    }
    export default observer(Form)

Enter fullscreen mode Exit fullscreen mode

Mobx 的文档写得很好,包含了很多最佳实践。

您可以在这里找到此示例的所有代码——

就到这里啦!感谢阅读!😃

文章来源:https://dev.to/rosyshrestha/build-your-first-app-with-mobx-and-react-4896