React - Json 除非我使用 SetTimeout 函数,否则架构表单下拉列表不会初始加载

React - Json Schema Form dropdowns won't load initally unless I use SetTimeout function

这让我和我的团队抓狂。这是相关代码。

在组件的 CDM 中,我们有:

    componentDidMount() {
    this.getContextID();
    this.getConsumerID();
    this.getEnvType();

    //setTimeout(() => this.setState({ populatedMultiSchema: this.multiSchema }), 200);
    //setTimeout(() => this.setState({ populatedMultiUISchema: this.multiUISchema }), 200);

     this.setState({ populatedMultiSchema: this.multiSchema });
     this.setState({ populatedMultiUISchema: this.multiUISchema });

}

所以列出的 3 种方法中的任何一种都将获取下拉列表的数据。这是一个例子(它们基本上都是一样的)。

getContextID() {
    contextIDOptions = [];
    console.log("CONVERT_TARGET::", this.props.fetchTarget)
    return (
        fetch(this.props.fetchTarget + "Configuration/ContextIDs", {
            method: 'GET',
            //mode: 'cors',
            credentials: 'include',
        }).then(response => {
            if (response.status >= 400) {
                this.setState({
                    value: 'no response - status > 400'
                });
                throw new Error('no response - throw');
            }
            return response.json()
        }).then(function (json) {
            for (var contextID = 0; contextID < json.List.length; contextID++) {
                contextIDOptions.push(json.List[contextID]);
            }
            this.setState({ contextIDArray: contextIDOptions });

            console.log("got contextIDs");
        }.bind(this)).catch(() => {
            this.setState({
                value: 'no response - cb catch'
            })
        })
    )
}

所以我们将那里的状态设置为'contextIDArray'。

然后 JSON 模式表单通过它的 multiUISchema 对象引用了这些帮助设置表单值的小部件。

ContextIDWidget = (props) => {
    return (
        <div>
            <input type="text" placeholder="Select one..." className="form-control" list="contextIDSelect" onChange={(event) => props.onChange(event.target.value)} />
            <datalist id="contextIDSelect">
                {this.state.contextIDArray.map((value, index) => { return <option key={index} value={value}>{value}</option> })}
            </datalist>
        </div>
    )
}

这是 multiUISchema 对象(对本次讨论很重要的部分)

multiUISchema = {
    file: {
        'ui:widget': this.MultiFileWidget,
        classNames: "uiSchema"
    },

    contextID: {
        'ui:widget': this.ContextIDWidget,
        classNames: "uiSchema"
    },

}

最后在组件的 return 中。

        return (
        <div className="container" >
            <Form
                schema={this.state.populatedMultiSchema}
                uiSchema={this.state.populatedMultiUISchema}
                formData={this.state.formData}
                onChange={({ formData }) => { this.setState({ formData }); this.setState({ totalFileSize: this.getMultiFileSize() }); this.checkConversionSupport() }}
                onSubmit={this.handleSubmit}
            >

长话短说,如果我在表单中使用状态对象,并且我对我使用的对象执行 setState。为什么我第一次加载组件时总是得到一个空白的下拉列表。 DOM(在本例中为下拉列表)是否应该在状态对象更改时使用来自提取的更新数据重新绘制?我的控制台日志在我的检查 window 中显示了获取的数据,因此我知道数据已被获取。这是选项卡组件。如果我离开选项卡或导航到我的 SPA 中的另一个页面,然后返回到该页面,则下拉菜单将全部加载。但是我永远无法让它最初加载,除非我在 CDM 中设置这些超时而不是仅仅设置状态。

   setTimeout(() => this.setState({ populatedMultiSchema: this.multiSchema }), 200);
      setTimeout(() => this.setState({ populatedMultiUISchema: this.multiUISchema }), 200);

我知道 post 很长,但我觉得我需要包含所有部分才能获得帮助。我可以向您保证,我们已经努力解决这个问题一个多星期了。我们欢迎任何意见。谢谢!

我对你的代码库不是很熟悉。但它看起来像是与异步请求有关的东西。这里:

this.setState({ populatedMultiSchema: this.multiSchema });
this.setState({ populatedMultiUISchema: this.multiUISchema });

这两行将在这些行之前执行:

this.getContextID();
this.getConsumerID();
this.getEnvType();

但您希望它们以相反的顺序执行。不。您的 getContextID 方法向服务器发出请求。 Javascript 是异步的。但是,通过在 asynchronous 函数中使用 await 表达式,您可以暂停执行并等待 Promise

所以,只需更新您的 componentDidMount 方法如下:

async componentDidMount() {
    await this.getContextID();
    await this.getConsumerID();
    await this.getEnvType();

    this.setState({ populatedMultiSchema: this.multiSchema });
    this.setState({ populatedMultiUISchema: this.multiUISchema });

}

Here i created a Codepen 关于 async/await 的用法。评论里有一些细节。想怎么玩就怎么玩。

即使你的问题不是主要由这个引起的,这种方法也更好。您应该使用 async/await or Promise 来处理网络请求。