我遇到了mobx的问题。实际上,我有一个简单的聊天室,使用我自己定义的WebSocketService。在此服务与我的API抽象交换时,聊天本身会处理消息的存储。
main.tsx(入口点)
import React from 'react';
import ReactDOM from 'react-dom';
import {App} from './app';
import {createBrowserHistory} from 'history';
import {createStores} from "app/stores";
import {Provider} from "mobx-react";
import {LoggedUserModel} from "app/models";
import {newUUID} from "app/utils/UUID";
// prepare MobX stores
const history = createBrowserHistory();
const rootStore = createStores(history, new LoggedUserModel(newUUID(), 'User'));
ReactDOM.render(
<Provider {...rootStore}>
<App history={history}/>
</Provider>,
document.getElementById('root')
);
index.tsx(应用程序)
import * as React from 'react';
import {Router, Route, Switch} from 'react-router';
import { hot } from 'react-hot-loader/root';
import { Chat } from "app/containers/Chat";
// render react DOM
export const App = hot(({history}) => {
return (
<Router history={history}>
<Switch>
<Route path="/" component={Chat}/>
</Switch>
</Router>
);
});
Chat.tsx
import * as React from 'react';
import style from './style.css'
import {RouteComponentProps} from 'react-router';
import ChatChannelMessagesList from "app/components/Chat/ChatChannelMessagesListComponent";
import ChatChannelInput from "app/components/Chat/ChatChannelInputComponent";
import {inject, observer} from "mobx-react";
import {STORE_LOGGED_USER, STORE_MESSAGES} from "app/constants";
import {LoggedUserStore, MessagesStore} from "app/stores";
import {newUUID} from "app/utils/UUID";
import {MessageModel} from "app/models";
import WebSocketService from "app/services/webSocketService";
export interface ChatProps extends RouteComponentProps<any> {
[STORE_LOGGED_USER]: LoggedUserStore;
[STORE_MESSAGES]: MessagesStore;
}
export interface ChatState {
WebSocketService: WebSocketService
}
@inject(STORE_LOGGED_USER, STORE_MESSAGES)
@observer
export class Chat extends React.Component<ChatProps, ChatState> {
constructor(props: ChatProps, context: any) {
super(props, context);
this.state = {
WebSocketService: new WebSocketService(event => {
// add the new message to state
this.onMessage(event)
})
}
}
componentDidMount() {
this.state.WebSocketService.connect(this.props[STORE_LOGGED_USER].getLoggedUser.id)
}
componentWillUnmount() {
this.state.WebSocketService.disconnect(this.props[STORE_LOGGED_USER].getLoggedUser.id)
}
addMessageToScreen(message: string) {
// add the new message to state
this.props[STORE_MESSAGES].addMessage(new MessageModel(newUUID(), message));
console.log(this.getMessages())
}
onMessage(event) {
console.log(event.data);
this.addMessageToScreen(event.data)
}
sendMessage(input: string) {
this.addMessageToScreen(input);
this.state.WebSocketService.message(this.props[STORE_LOGGED_USER].getLoggedUser.id, input)
}
getMessages() {
return this.props[STORE_MESSAGES].getMessages
}
render() {
return (
<div id={"chat"} className={style.container}>
<div className="rox">
<div className="row-offset-11 row-11 row-sm-11 row-md-11 row-lg-11 row-xl-11">
<ChatChannelMessagesList messages={this.getMessages()}/>
</div>
<div className="row-1 row-sm-1 row-md-1 row-lg-1 row-xl-1">
<ChatChannelInput onSendClicked={(input: string) => this.sendMessage(input)}/>
</div>
</div>
</div>
);
}
}
export default Chat
我没有错误,一切正常,但是更新并没有刷新屏幕,我也不明白为什么。
邮件存储
import { observable, computed, action } from 'mobx';
import { MessageModel } from 'app/models';
export class MessagesStore {
constructor(items: Array<MessageModel>) {
this.Messages = items;
}
@observable public Messages: Array<MessageModel>;
@computed
get getMessages() {
return this.Messages;
}
@action
addMessage = (item: MessageModel): void => {
this.Messages.push(item);
};
@action
setMessages = (items: Array<MessageModel>): void => {
this.Messages = items
};
@action
deleteMessage = (id: string): void => {
this.Messages = this.Messages.filter((Message) => Message.id !== id);
};
}
export default MessagesStore;
我想我已经准备好一切,随时询问更多详细信息,并感谢您的帮助!
免责声明:首次使用MobX
答案 0 :(得分:3)
有两个问题:
在您的MessagesStore
中,您没有正确使用 @observable 。
@observable 使字段的初始值可观察。但是,您将立即用MessageStore
的构造函数中的常规不可观察数组替换此值。因此MessageStore.Messages
将永远不可观察,也不会触发观察者组件的任何重新渲染。
您应该做的是使用一个数组初始化MessageStore.Messages
字段,该数组将是可观察的,并且永远不要为该字段分配不可观察的数组。
如果您需要用另一个数组替换此数组,就像在MessageStore
的构造函数和MessageStore.deleteMessage
中所做的那样,则应使用MobX的可观察数组的.replace
方法,该方法将替换可观察数组的内容以及另一个数组的内容。
export class MessagesStore {
constructor(items: Array<MessageModel>) {
this.Messages.replace(items);
}
@observable public Messages = new Array<MessageModel>();
...
@action
deleteMessage = (id: string): void => {
this.Messages.replace(this.Messages.filter((Message) => Message.id !== id));
};
}
那样,MessageStore.Messages
将是可观察的,并且观察到其更改的组件将按预期方式重新呈现。
第二个问题是您的Chat
组件未观察到MessageStore.Messages
的更改,因此所说的更改将永远不会触发Chat
的重新渲染。原因如下(来自MobX's documentation):
MobX可以做很多事情,但是不能使原始值可观察(尽管它可以将它们包装在一个对象中,请参阅框内的可观察对象)。因此,不是可观察的值,而是对象的属性。这意味着@observer实际上会对您取消引用值这一事实做出反应。
<ChatChannelMessagesList messages={this.getMessages()}/>
在这里,您只将MessageStore.Messages
作为值传递给ChatChannelMessagesList
。您没有取消引用它(这意味着访问它的任何属性,例如迭代数组),因此Chat
即使标记为@observer,也不会对MessageStore.Messages
的内容更改做出反应
将对这些更改做出反应的组件是第一个实际访问数组内容的组件,该组件可能是ChatChannelMessagesList
。我的猜测是该组件未标记为@observer,并且从不对消息更新做出反应,这就是为什么您看不到任何重新渲染的原因。
您有两种解决方案:
-在MessageStore.Messages
内取消引用(访问Chat
的内容),以便在有新消息时重新提交
-使ChatChannelMessagesList
(或第一个引用MessageStore.Messages
的组件)@observer,使其对更改做出反应。
第二种方法更好,因为您应该始终尽可能晚地取消引用可观察对象,以避免不必要地重新渲染不依赖于这些更改的父级组件。在您的情况下,Chat
的内容发生更改时重新渲染MessageStore.Messages
是没有用的,因为Chat
仍然会渲染相同的内容。
摘要: