首页 > 解决方案 > React Store 没有第一次返回 getMethod 的值

问题描述

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

我的商店课程

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);
        }
    }

}

子组件

这是我使用 Store 的地方,当我使用useEffectHook 时,它实际上会填充数据,但是当我在内部onClick函数中运行时,它不会填充值。

我的理解是 AxiosGET调用按预期执行,它还返回值并将其分配给serviceTemplateDetail变量。但是当我通过getServiceTemplateDetail方法访问该值时,它第一次返回 null,当我下次单击 Import 按钮时,它返回从GETRequest返回的旧值

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
}

标签: javascriptreactjsreact-hooksmobxmobx-react

解决方案


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

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

希望这是有道理的。基本上你只需要阅读闭包以及它们是如何工作的。

React 文档中有一个小指南

例如,您可以做的是删除值的解构并根据需要取消引用它们,如下所示:

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
  });

推荐阅读