在 JS class 的构造函数中调用正在获取初始数据的异步函数是否可以?

Is it okay to call an async function that is fetching initial data in the constructor of a JS class?

我正在使用 Mobx 和 Mobx React Lite 构建示例应用程序,以了解如何使用此状态管理库。当用户访问页面时,应用程序需要加载问题。这样在构造函数中调用初始加载数据可以吗?这会不会以某种方式导致错误?

我担心它可能会再次获取,但我不确定这可能是我应该担心的唯一问题。

如果这有风险,您还会建议什么其他模式?

我的另一个想法是在他们单击“开始测验”按钮时断章取义,并在获取数据时显示加载屏幕。这很可能应该如何发生,但我或多或少只是想知道我所做的是否也很好。

import { observable, action } from "mobx";
import { getQuestions } from "../api/api";

export interface Question {
    category: string;
    type: string;
    question: string;
    correct_answer: string;
    incorrect_answers: string[];
}

export class TriviaStore {

    constructor() {
        // Is this bad?
        (async() => {
            await this.loadQuestions();
        })()
    }

    @observable questions: Question[] = [];
    @observable currentQuestion: number = 0;

    @action
    async loadQuestions() {
        let questions: Question[] = await getQuestions();
        this.questions = questions;
    }
    @action
    nextQuestion() {
        this.currentQuestion++;
    }
}

存储仅在上下文提供程序中实例化一次,如下所示:

import React from 'react';
import { TriviaStore } from '../stores/TriviaStore';

/**
 * Store Types.
 * Add any new stores here so the context type is updated.
 */
type RootStateContextValue = {
    triviaStore: TriviaStore;
}

const RootStateContext = React.createContext<RootStateContextValue>({} as RootStateContextValue);

/**
 * Stores
 * Use this area to create instances of stores to pass down.
 */
const triviaStore = new TriviaStore();

/**
 * Root State Provider
 * @returns global state context wrapper.
 */
export const RootStateProvider: React.FC<React.PropsWithChildren<{}>> = ({children}) => {
    // Finally pass new stores into the object below to send them to global state.
    return (
        <RootStateContext.Provider
            value={{
                triviaStore,
            }}
        >
            {children}
        </RootStateContext.Provider>
    );
}

export const useGlobalState = () => React.useContext(RootStateContext);

尽管 React 允许您获取一些数据并且不会在构造函数中引起任何错误,但 React 开发人员不建议这样做,这不是一个好的做法。您应该改为在 componentDidMount() 中获取数据。

如果您的设计需要在加载页面后进行多次抓取,您可以尝试在 componentDidUpdate() 中进行并更新您的初始状态。

在我看来,在加载所有数据之前初始化存储是可以的。您可以将加载状态直接添加到商店。将异步函数作为一种方法放在商店中是个好主意。虽然我认为立即执行的异步函数包装器没有效果,并且会在加载问题之前初始化存储。看这个例子:

@observable loading = true

constructor() {
  // Same as with your wrapper, constructor cannot be made async.
  this.loadQuestions()
}

@action
async loadQuestions() {
  let questions: Question[] = await getQuestions()
  // Newer versions of MobX will warn if runInAction missing after async call.
  runInAction(() => {
    this.questions = questions
    this.lading = false
  })
}

我认为不太好的部分是将 MobX 存储与 React 上下文混合,我认为这不是必需的,因为您已经可以从任何地方直接导入存储。