使用 Mobx 和 React 构建你的第一个应用
除了Redux和Context API之外, MobX是另一个可用于 React 应用的状态管理库。然而,MobX 不仅适用于 React,它也适用于其他用于 Web 应用前端的 JavaScript 库和框架。MobX 5 及更高版本可在任何支持 ES6 代理的浏览器上运行。
主要概念
以下是mobx的主要概念:
可观测的
可观察对象允许我们将任何数据结构或属性转换为可观察状态,以便其他事物可以跟踪这些可观察的变化和值。
行动
该操作允许我们更改可观察对象的状态(即值)。为确保代码的可扩展性,状态数据不应在操作之外被修改。
计算
计算属性允许我们根据状态变化推导出一些值。这些计算值是通过对可观察对象执行某种计算而获得的。
观察者
观察者机制允许我们跟踪可观察对象的变化,以便 React 在任何变化发生时都能收到通知并开始重新渲染。它们由 mobx-react 包提供。
店铺
store 是数据源。与 Redux 不同,在 MobX 中,数据和修改数据的函数都存储在 store 中。因此,store 可以包含 Observable 和 Action。
简而言之,MobX 基于可观察对象-观察者流程工作。你声明一些数据为可观察对象,当这些数据发生变化时,所有使用该数据的观察者都会收到通知。
现在让我们把这些概念付诸实践。
我们将创建一个简单的应用程序,用户可以在上面对图片做出反应并发表评论,类似于Facebook。这是演示链接。
项目设置
假设您已具备 React 的相关知识,则需要在您的计算机上安装NPM和Node.js。
我正在使用自定义的 webpack 配置,并设置项目以启用装饰器。别担心,还有一种不使用装饰器的替代方法。在这个例子中,我还是使用了装饰器,因为它更简洁。不过我也会提到其他方法。如果您使用的是 create-react-app,则可以跳过这些设置步骤。
master从此仓库拉取分支进行初始设置。
运行yarn以安装依赖项并使用.该应用程序将在http://localhost:8080yarn start上运行。
装饰师的准备工作
要启用 ESNext 装饰器,需要以下插件。
yarn add --dev @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators
然后将以下配置添加到 .babelrc 文件中。
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
]
]
款式
请拉取设计分支以获取样式。所有样式都位于css目录下的相应文件夹内src。以下是我们的应用组件示意图。
- 卡片组件包含:
-
随机生成的图像。
-
计数组件用于跟踪点赞数和评论数。
-
包含点赞和评论按钮的按钮组件。
-
包含用于发表评论的输入字段的表单组件。
-
评论组件,包含评论列表。
安装依赖项
安装mobx状态管理解决方案和mobx-react库,将状态层连接到 React 视图层。
yarn add mobx mobx-react
现在我们将开始使用 Mobx 添加功能。
店铺
首先,我们要创建一个Store.jsx子store文件夹。
import { observable, action } from 'mobx'
class Store {
@observable likesCount = 12
@action updateCount{
this.likesCount++;
}
}
const storeInstance = new Store()
export default storeInstance;
在这里,我们创建了一个 Store 类,它有likesCount一个可观察的状态,updateCount一个用于修改状态的操作,然后导出了 Store 的一个新实例。
如果你的配置不支持装饰器,则上述代码可以重写为:
import { decorate, observable } from "mobx";
class Store {
likesCount = 12;
updateCount{
this.likesCount++;
}
}
decorate(Store, {
likesCount: observable,
updateCount: action
})
然后,我们通过使用 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'));
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>
)
}
请记住,可观察状态只能通过操作进行修改。因此,为了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>
如果你点击“点赞”按钮,不会看到任何变化。
为了观察并响应函数组件的变化,我们可以将组件包装在观察者函数中,或者实现 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>
))
现在点击按钮后,点赞数会更新。
评论
我们开始整理评论区吧。
数组数据结构也可以是可观察的。让我们创建一个可观察comments字段。在代码中添加以下内容Store.jsx。
@observable comments = ["Wow", "awesome"]
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>
)
}
我们还需要允许用户通过表单添加评论。
首先,让我们postComment在 store 中创建一个名为 `add` 的操作,该操作会将新评论添加到之前的评论数组中。在 `.` 中添加以下代码行Store.jsx。
@action postComment(comment){
this.comments.push(comment)
}
然后按如下方式更新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>
)
}
}
postComment在这里,我们只是创建了一个函数,当用户提交评论时调用商店的操作,并在提交后将输入字段设置为空。
为了在添加新评论时更新评论组件,我们需要像对 Count 组件那样,将 Comments 组件设置为观察者。因此,需要Comments.jsx将要返回的内容包装起来useObserver。另外,别忘了导入useObserver。
return useObserver(() => (
<table className="table">
<tbody>
{
store.comments.map((comment, index) => {
...
...
}
</tbody>
</table>
)
)
现在,如果您输入任何评论并按下回车键,您的评论列表将自动更新。
让我们在点击评论按钮时聚焦到输入框。我们可以直接使用 HTML DOM 的 focus() 方法。但首先,让我们给输入框添加一个 id。
<input type="text" id={'comment'} className="form-control" placeholder={"Write a comment ..."}
ref={node => {this.comment = node;}} />
onClick然后,在组件的评论按钮处理程序中添加 focus 方法Buttons.jsx。
<button type="button" className="btn btn-light"
onClick={() => document.getElementById('comment').focus()}>
<i className="fa fa-comment-o" />
Comment
</button>
现在,当您点击评论按钮时,评论字段将获得焦点。
计算
为了获取评论数量,我们将创建一个commentsCountgetter 函数来计算可观察comments数组的长度。MobX 会在数组发生变化commentsCount时自动更新comments。请Store.jsx添加以下代码:
@computed get commentsCount(){
return this.comments.length;
}
然后只需更新以下几行即可Count.jsx。
<div className="col-sm" align="right">
{store.commentsCount} comments
</div>
你还会注意到,当你添加评论时,计数也会随之更新。
服务/API调用
在应用程序中,调用 API 和编写异步代码非常常见。由于这是一个自定义的 webpack 配置,要启用 async/await,请更新文件.babelrc如下。
"presets": [
["@babel/preset-env",
{
"targets": {
"node": "10"
}
}
],
"@babel/preset-react"
],
否则你可能会遇到这个错误
让我们在按钮点击时更改组件中的图像Card.jsx。我们将使用这个有趣且免费的 API 来获取《瑞克和莫蒂》动画剧集中的角色图像。更多详情请查看他们的文档。
从本节中你会发现,我们可以通过添加id参数来获取单个字符:/character/1
https://rickandmortyapi.com/api/character/1
我们创建一个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;
你也可以只运行回调函数中修改状态的部分。这里我们创建了一个操作来设置外部 URL fetchImage,然后根据需要调用了它。
class ImageStore {
...
@action async fetchImage() {
...
this.setImageUrl(data.image)
}
@action setImageUrl(url) {
this.imageUrl = url
}
}
然后是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>
))
);
}
好啦,大功告成!最终输出效果如下:
#笔记
将 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)
Mobx 的文档写得很好,包含了很多最佳实践。
您可以在这里找到此示例的所有代码——
就到这里啦!感谢阅读!😃
文章来源:https://dev.to/rosyshrestha/build-your-first-app-with-mobx-and-react-4896




