React Store 第一次没有 return getMethod 的值

React Store does not return getMethod's value first time

我是 React、Mobx 堆栈的新手。我遇到了一个问题,我的商店在第一次调用后没有立即反映它的价值。当我进行第二次调用时,它 returns 第一个值。我无法理解为什么会发生这种情况,因为我在 React、Mobx 堆栈方面还有很长的路要走。

我的商店Class

export default class ServiceTemplateStore {

    loadingInitial = false;

    serviceTemplateDetail: any | undefined;

    constructor() {
        makeAutoObservable(this)
    }

    setLoadingInitial = (state: boolean) => {
        this.loadingInitial = state;
    }


    get getServiceTemplateDetail() {
        return this.serviceTemplateDetail;
    }

    
    loadServiceTemplateDetail = async (id: string) => {
        this.loadingInitial = true;

        try {

            const serviceTemplateDetail = await agent.serviceTemplateDetail.details(id);

            runInAction(() => {
                console.log("Service loaded");
                this.serviceTemplateDetail = serviceTemplateDetail;
                console.log(this.serviceTemplateDetail); //Here I can see it immediately outputs the value.
                this.setLoadingInitial(false);
            });
            
        } catch (error) {
            console.log(error);
            this.setLoadingInitial(false);
        }
    }

}

子组件

这是我使用商店的地方,当我使用 useEffect 挂钩时它实际上填充了数据,但是当我 运行 在 onClick 函数内部时它不填充值。

我的理解是 Axios GET 调用按预期执行,它 returns 返回值并将其分配给 serviceTemplateDetail 变量。但是当我通过 getServiceTemplateDetail 方法访问该值时,它是第一次 returns null,当我下次单击“导入”按钮时,它 returns 从 [= 返回的旧值15=]请求

import DataGrid, { Selection } from "devextreme-react/data-grid";
import Popup, { Position, ToolbarItem } from "devextreme-react/popup";
import { Column } from "devextreme-react/tree-list";
import { observer } from "mobx-react-lite";
import React, { Component, useCallback, useEffect, useState } from 'react'
import { useStore } from "../store/Store";


export default observer(function ImportServiceTemplate(props: importServiceProp) {

    const [ serviceTemplateIds, setServiceTemplateIds] = useState([]);
    const { serviceTemplateStore } = useStore();
    const { serviceTemplateList, loadServiceTemplateList, loadServiceTemplateDetail, getServiceTemplateDetail, serviceTemplateDetail } = serviceTemplateStore;
    
    useEffect(() => {
        if (serviceTemplateList?.length === 0 || serviceTemplateList === undefined) loadServiceTemplateList();
    }, [loadServiceTemplateList])


    const closeButtonOption = {
        icon: 'close',
        text: 'Close',
        onClick: ()=>{
            props.onCloseImportModel();
        }
    };

    //Added a useEffect to test..
    //UseEffect returns the value but not my Import function below
    useEffect(() => {
        console.log("use effect");
        console.log(JSON.stringify(getServiceTemplateDetail));
    }, [getServiceTemplateDetail])
    

    //Actual function that needs to work.
    const importButtonOption = {
        icon: 'download',
        text: 'Import',
        onClick: () => {

            if(serviceTemplateIds){

                loadServiceTemplateDetail(String(serviceTemplateIds))
                .then(()=>{
                    console.log("Callback");
                    console.log(JSON.stringify(serviceTemplateDetail));     // Getting undefined as output.
                    props.importingServiceTemplate(getServiceTemplateDetail); //Passing the imported value to parent component
                });

            }
            
        }
    };

    const onSelectionChanged = (e: any) => {
        console.log("Processing", e.selectedRowKeys);
        setServiceTemplateIds(e.selectedRowKeys);
    };

    return (
        <>

            <Popup
                visible={props.isImportVisible}
                dragEnabled={false}
                closeOnOutsideClick={false}
                showCloseButton={false}
                showTitle={true}
                title="Service Template"
                width={800}
                height={280}>
                <Position
                    at="center"
                    my="center"
                />

                <DataGrid
                    id="serviceTemplateGrid"
                    key="ServiceTemplateId"
                    keyExpr="ServiceTemplateId"
                    focusedRowEnabled={true}
                    onSelectionChanged={onSelectionChanged}
                    selectedRowKeys={serviceTemplateIds}
                    dataSource={serviceTemplateList}
                    showRowLines={true}
                    showBorders={true}>

                    <Selection mode="multiple" showCheckBoxesMode="onClick" />
                    <Column dataField="Name" caption="Name" />
                    <Column dataField="CustomerName" caption="Customer Name" />
                    <Column dataField="BaseCurrencyCode" caption="Currency" />
                    <Column dataField="Description" caption="Description" />

                </DataGrid>
    
                <ToolbarItem
                    widget="dxButton"
                    toolbar="bottom"
                    location="before"
                    options={closeButtonOption}
                />

                <ToolbarItem
                    widget="dxButton"
                    toolbar="bottom"
                    location="after"
                    options={importButtonOption}
                />
                
                <div>{JSON.stringify(getServiceTemplateDetail)}</div>

            </Popup>
        </>
    )

})

interface importServiceProp{
    isImportVisible: boolean;
    onCloseImportModel: Function
    importingServiceTemplate: any
}

这不是 React 或 MobX 的问题,只是一个常规的 javascript 闭包。

当您创建 importButtonOption 函数时,它 remembers 它周围的所有变量,并且 serviceTemplateDetail 第一次等于未定义。因此,在您调用 loadServiceTemplateDetail 后,serviceTemplateDetail 在商店中发生了更改,但在 importButtonOption 函数内部它仍然是一个旧值,在创建函数时是 remembered

希望它有意义。基本上你只需要阅读闭包及其工作原理。

React docs

里面有个小攻略

例如,您可以做的是删除值的解构并根据需要取消引用它们,就像这样:

loadServiceTemplateDetail(String(serviceTemplateIds))
  .then(()=>{
      console.log("Callback");
      console.log(JSON.stringify(serviceTemplateStore .serviceTemplateDetail));     // reference it from the store to get actual value
      props.importingServiceTemplate(getServiceTemplateDetail); //Passing the imported value to parent component
  });