【问题标题】:React Store does not return getMethod's value first timeReact Store 没有第一次返回 getMethod 的值
【发布时间】:2021-08-10 08:10:58
【问题描述】:

我是 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 的地方,当我使用 useEffect Hook 时,它实际上会填充数据,但是当我在 onClick 函数中运行时,它不会填充值。

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

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
}

【问题讨论】:

    标签: javascript reactjs react-hooks mobx mobx-react


    【解决方案1】:

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

    当你创建importButtonOption 函数时,它周围的所有变量remembers,并且serviceTemplateDetail 在第一次等于未定义。因此,在您调用 loadServiceTemplateDetail 后,serviceTemplateDetail 在 store 中发生了更改,但在 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
      });
    

    【讨论】:

    • 你是对的。我还有一个疑问。在props.importingServiceTemplate(serviceTemplateStore.getServiceTemplateDetail); serviceTemplateStore 也被创建为一个 Store 变量。这怎么不保持旧值?
    • 简单来说,因为serviceTemplateStore 不是原始值,它是一个对象,当您将对象分配给变量时,您实际上是在将“引用”分配给“活动”对象,并且它总是“最新的”。
    猜你喜欢
    • 2021-07-10
    • 1970-01-01
    • 2017-04-13
    • 2014-06-14
    • 2018-11-01
    • 1970-01-01
    • 2015-06-04
    • 1970-01-01
    • 2021-09-30
    相关资源
    最近更新 更多