【问题标题】:ReactJs, TypeScript, MobX - Unable to get MobX refreshing screen on store data updateReactJs、TypeScript、MobX - 无法在商店数据更新时获得 MobX 刷新屏幕
【发布时间】:2023-04-01 19:45:02
【问题描述】:

我遇到了 mobx 的问题。事实上,我有一个简单的聊天室,它使用我自己定义的 WebSocketService。虽然此服务抽象了与我的 API 的交换,但聊天本身处理消息的存储。

ma​​in.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

我没有错误,一切运行顺利,但更新不刷新屏幕,我不明白为什么..

messageStore

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

【问题讨论】:

    标签: reactjs typescript mobx


    【解决方案1】:

    有两个问题:

    在您的 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 可以做很多事情,但它不能使原始值可观察(尽管它可以将它们包装在一个对象中,参见装箱的 observables)。所以不是可观察的值,而是对象的属性。这意味着 @observer 实际上会对您取消引用一个值这一事实做出反应。

      <ChatChannelMessagesList messages={this.getMessages()}/>
    

    在这里,您只是将MessageStore.Messages 作为值传递给ChatChannelMessagesList。您没有取消引用它(这意味着访问它的任何属性,例如迭代数组),所以Chat,即使标记为@observer,也不会对MessageStore.Messages 的内容更改做出反应。

    将对这些更改做出反应的组件是第一个实际访问数组内容的组件,这可能是ChatChannelMessagesList。我的猜测是这个组件没有被标记为@observer,并且永远不会对消息更新做出反应,这就是你没有看到任何重新渲染的原因。

    对于这个问题,您有两种解决方案: - 在Chat 中取消引用(访问)MessageStore.Messages 的内容,因此当有新消息时它会重新呈现 - 使ChatChannelMessagesList(或取消引用MessageStore.Messages的第一个组件)@observer,因此它对更改做出反应。

    第二种解决方案更好,因为您应该始终尽可能晚地取消引用您的可观察对象,以避免对不依赖于这些更改的父组件进行无用的重新渲染。在您的情况下,当MessageStore.Messages 的内容发生变化时重新渲染Chat 是没有用的,因为Chat 无论如何都会渲染同样的东西。

    总结:

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

    【讨论】:

    • 感谢 Alex 这个详细的回答 :) 真的很有帮助!!
    猜你喜欢
    • 2021-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多