Javascript 确保加载所有子项时仅显示父项

Javascript 确保加载所有子项时仅显示父项,javascript,reactjs,Javascript,Reactjs,我是一个新的反应者,我正在努力应对以下问题。我在react中创建了一个表单,该表单包含一个下拉列表。我需要在多个页面中重用该下拉列表,所以我想让它成为一个完全负责获取所有数据的组件 在表单组件中,我获取所有数据,其中一个字段是selectedServerDataId。selectId字段包含需要在下拉列表中选择的值的ID <snip includes/> class Arrival extends React.PureComponent { constructor(prop

我是一个新的反应者,我正在努力应对以下问题。我在react中创建了一个表单,该表单包含一个下拉列表。我需要在多个页面中重用该下拉列表,所以我想让它成为一个完全负责获取所有数据的组件

在表单组件中,我获取所有数据,其中一个字段是selectedServerDataId。selectId字段包含需要在下拉列表中选择的值的ID

<snip includes/>

class Arrival extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            formData: {}
        };
    }

   async componentDidMount() {
        await fetch(urls.addEditUrl)
            .then(response => response.json())
            .then(data => {
                this.setState({ formData: data });
            });
    }

    <snip some setstate helpers/>

    render() {
        const { formData } = this.state;
        return (
                <Form >
                <Selector label="select"
                    onChange={this.UpdateFormSelectData.bind(this, 'selectedServerDataId')}
                    value={formData.selectedServerDataId}/>
                <DateItem label="date"
                          allowClear={true}
                          value={formData.date}
                          onChange={this.updateFormDateData.bind(this, 'date')}/>
                <snip more fields.../>
            </Form>);
    }
}

export default Arrival;

类到达扩展了React.PureComponent{
建造师(道具){
超级(道具);
此.state={
formData:{}
};
}
异步组件didmount(){
等待获取(URL.addEditUrl)
.then(response=>response.json())
。然后(数据=>{
this.setState({formData:data});
});
}
render(){
const{formData}=this.state;
返回(
);
}
}
导出默认到达;
父组件中的fetch检索编辑表单的数据,包括selectedServerDataId。我的子组件如下所示:

<snip usings />

class ServerDataSelector extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            options: [],
        };
    }

    async componentDidMount() {
        await fetch(URLS.GetServerData)
            .then(response => response.json())
            .then(data => {
                this.setState({ options: data });
            });
    }

    render() {
        const { options } = this.state;
        return (
            <FormItem {...formItemLayout} label={t(this.props.label)} >
                <Select setValue={this.props.onChange} getValue={() => this.props.value} selectedOption={this.props.value} name={this.props.label}>
                        {options.map(d => <Option key={d.id} value={d.id}>{d.value}</Option>)}
                </Select>
                  //TODO add model window to manipulate list.
                </FormItem>);
    }
}

export default ServerDataSelector 
ServerDataSelector { Selector }

类ServerDataSelector扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
选项:[],
};
}
异步组件didmount(){
等待获取(URL.GetServerData)
.then(response=>response.json())
。然后(数据=>{
this.setState({options:data});
});
}
render(){
const{options}=this.state;
返回(
this.props.value}selectedOption={this.props.value}name={this.props.label}>
{options.map(d=>{d.value}}
//TODO添加模型窗口以操作列表。
);
}
}
导出默认服务器数据选择器
ServerDataSelector{Selector}
当前,当页面呈现时,我首先在下拉列表中看到选定值的ID,然后在一瞬间看到实际的选定值标签


是否有办法确保父组件仅在子组件完全加载完成时渲染

这里的主要问题是,有两个组件具有严格的呈现层次关系(父级->子级),但数据获取责任相同

这导致您必须对多个组件之间按特定顺序进行的数据检索做出反应,这是一个非常重要的并发问题。一种解决方案是使用共享状态容器(使用或),并让这两个组件将其结果保存到此共享状态存储桶中,然后每当存储桶包含所有数据时,您都会将其显示出来(在此之前,您可以显示一个微调器)

一个更简单的解决方案是只需将抓取移动到其中一个组件中,完全消除这种分离数据抓取责任,并允许抓取数据的组件也控制渲染

一种方法是获取父级中的所有数据,并使子级仅为接收数据时呈现的纯组件:

class Arrival extends React.Component {
  // ...

  async componentDidMount() {
    const [formData, options] = await Promise.all([
      fetch(urls.addEditUrl).then(response => response.json()),
      fetch(URLS.GetServerData).then(response => response.json())
    ]);

    this.setState({ formData, options });
  }

  render() {
    const { formData, options } = this.state;
    return (
      <Form>
        {/* ... */}
        {formData &&
          options && (
            <Selector
              label="select"
              options={this.state.options}  {/* pass the options here */}
              onChange={this.UpdateFormSelectData.bind(
                this,
                "selectedServerDataId"
              )}
              value={formData.selectedServerDataId}
            />
          )}
      </Form>
    );
  }
}
您可以选择显示微调器或加载指示器:

...
{
  formData && options ? (
    <Selector
      label="select"
      onChange={this.UpdateFormSelectData.bind(this, "selectedServerDataId")}
      options={this.state.options}
      value={formData.selectedServerDataId}
    />
  ) : (
    <Spinner />
  );
}
。。。
{
formData&选项(
) : (
);
}
或者,您可以让子对象获取所有数据,而父对象只呈现子对象

class ServerDataSelector extends React.Component {
  // ...

  async componentDidMount() {
    const [formData, options] = await Promise.all([
      fetch(urls.addEditUrl).then(response => response.json()),
      fetch(URLS.GetServerData).then(response => response.json())
    ]);

    this.setState({
      value: formData.selectedServerDataId,
      options
    });
  }

  render() {
    return (
      <FormItem {...formItemLayout} label={t(this.props.label)}>
        <Select
          setValue={this.props.onChange}
          getValue={() => this.state.value}  {/* use the value from the state */}
          selectedOption={this.state.value}  {/* use the value from the state */}
          name={this.props.label}
        >
          {this.state.options.map(d => (
            <Option key={d.id} value={d.id}>
              {d.value}
            </Option>
          ))}
        </Select>
      </FormItem>
    );
  }
}
classserverdataselector扩展React.Component{
// ...
异步组件didmount(){
const[formData,options]=wait Promise.all([
fetch(url.addEditUrl).then(response=>response.json()),
获取(url.GetServerData).then(response=>response.json())
]);
这是我的国家({
值:formData.selectedServerDataId,
选择权
});
}
render(){
返回(
this.state.value}{/*使用state*/}中的值
selectedOption={this.state.value}{/*使用状态中的值*/}
name={this.props.label}
>
{this.state.options.map(d=>(
{d.value}
))}
);
}
}
您还可以在子级中添加微调器,以防止页面抖动,并实现更好的用户体验:

render() {
  if (!this.state.value || !this.state.options) {
    return <Spinner />;
  }

  return (
    <FormItem {...formItemLayout} label={t(this.props.label)}>
      <Select
        setValue={this.props.onChange}
        getValue={() => this.state.value}
        selectedOption={this.state.value}
        name={this.props.label}
      >
        {this.state.options.map(d => (
          <Option key={d.id} value={d.id}>
            {d.value}
          </Option>
        ))}
      </Select>
    </FormItem>
  );
}
render(){
如果(!this.state.value | |!this.state.options){
返回;
}
返回(
this.state.value}
selectedOption={this.state.value}
name={this.props.label}
>
{this.state.options.map(d=>(
{d.value}
))}
);
}

经过多次尝试和错误后,我找到了一种解决方法

在我的父状态中,我添加了以下对象:

this.state = {
            formData: null,
            loadedComponents: {
                parentComponentIsLoaded: false,
                childComponent1IsLoaded: false,
                childComponent2IsLoaded: false,
            }
        };
然后我在我的父对象中添加了以下两种方法

   componentLoaded = (field, data) => {
    const { loadedComponents } = this.state;
    var fieldName = field + "IsLoaded";

    this.setState({ loadedComponents: { ...loadedComponents, [fieldName]: true } });
}

allComponentsLoaded() {

    const { loadedComponents } = this.state;
    console.log(loadedComponents);
    for (var o in loadedComponents) {
        if (!loadedComponents[o]) return false;
    }
    return true;
}
将渲染方法更改为:

        return (
        formData ?
            <Form className={this.allComponentsLoaded() ? '' : 'hidden'} >
                <ChildComponet1 {someprops} isLoaded={this.componentLoaded}/>
                <ChildComponent2 {someprops} isLoaded={this.componentLoaded}/>
            <snip />
        </Form> : <div />);

也许这是一个非常糟糕的react设计,您应该实际使用MEM035的答案,但它确实有效

感谢您提供了广泛的答案。你的回答让我觉得我试着用一种不是反应的方式来做事情。作为一名后端程序员,我总是尝试制作可重用的组件,并承担1项责任。我将查看提供的库,否则我将使用父加载所有路径。虽然我希望我能找到一种方法来处理这个问题,因为这可以在我正在制作的应用程序中为我节省大量前端代码。很高兴能帮助mate。react中的组件应该是可重用的,您的想法是正确的。无论技术部门(后端、前端等)如何,只要有一个职责,这都是一个坚实的软件工程原则。在所有这些问题上,我都非常同意你的看法。方法中唯一的问题是绘制组件边界的位置。我的建议是基于您给出的小代码示例,但我得到的最好建议是采纳您关于可重用性和单一责任的想法,并改变您的c语言
        return (
        formData ?
            <Form className={this.allComponentsLoaded() ? '' : 'hidden'} >
                <ChildComponet1 {someprops} isLoaded={this.componentLoaded}/>
                <ChildComponent2 {someprops} isLoaded={this.componentLoaded}/>
            <snip />
        </Form> : <div />);
this.props.isLoaded('childComponet1', true)