ReactJs,TypeScript,MobX-无法在商店数据更新时获得MobX刷新屏幕

时间:2019-09-13 15:58:11

标签: reactjs typescript mobx

我遇到了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

1 个答案:

答案 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仍然会渲染相同的内容。

摘要:

  • 不要用新数组重新分配包含可观察数组的字段,而是更改可观察数组的内容。
  • 当您访问可观察对象的属性而不是其值时,
  • @observer组件将重新渲染。 @observer组件应该是那些访问这些属性的组件。