Interupt代码并在循环中等待用户交互-React

时间:2019-06-07 15:38:03

标签: reactjs typescript

我正在尝试在我的react应用程序中实现“全部添加”按钮。为此,我将此函数传递给按钮的onClick方法:

for (element in elements) {
    await uploadfunction(element)
}

const uploadfunction = async (element) => {
    if (valid) {
        // await performUpload(element)
    }
    else if (duplicate) {
        //show dialog to confirm upload - if confirmed await performUpload(element)
    }
    else {
        // element not valid set state and show failed notification
    }
}

const performUpload = async (element) => {
    // actual upload
    if(successful){
        // set state
    }else{
        // element not successful set state and show failed notification
    }
}

上传功能可以具有三种不同的行为:

  • 将元素添加到数据库并更新状态
  • 无法添加元素并更新状态
  • 使用“反应对话框”组件提示用户要求确认以添加重复元素并相应地更新状态

我现在的问题是,因为我正在使用for循环,尽管使用了Async / await,但在确认的情况下,我似乎无法等待用户交互。

我目前的行为:

  • 无论结果如何,for循环都会移至下一个元素
  • 对话框仅显示一秒钟,然后消失,并且不等待用户交互

想要的行为:

  • 等待用户交互(放弃/确认)对话框以执行循环中的下一个动作。

如何在没有Redux的情况下使用React实现这一目标?

1 个答案:

答案 0 :(得分:0)

这里是一个可能为您带来灵感的组件示例。 您可以将其拆分为不同的组件。

class MyComponent extends Component {
  state = {
    items: [{
      // set default values for all booleans. They will be updated when the upload button is clicked
      isValid: true,
      isDuplicate: false,
      shouldUploadDuplicate: false,
      data: 'element_1',
    }, {
      isValid: true,
      isDuplicate: false,
      shouldUploadDuplicate: false,
      data: 'element_1',
    }, {
      isValid: true,
      isDuplicate: false,
      shouldUploadDuplicate: false,
      data: 'element_2',
    }],
    performUpload: false,
  };

  onUploadButtonClick = () => {
    this.setState(prevState => ({
      ...prevState,
      items: prevState.items.map((item, index) => ({
        isValid: validationFunction(),
        isDuplicate: prevState.items.slice(0, index).some(i => i.data === item.data),
        shouldUploadDuplicate: false,
        data: item.data
      })),
      performUpload: true,
    }), (nextState) => {
      this.uploadToApi(nextState.items);
    });
  };

  getPromptElement = () => {
    const firstDuplicateItemToPrompt = this.getFirstDuplicateItemToPrompt();
    const firstDuplicateItemIndexToPrompt = this.getFirstDuplicateItemIndexToPrompt();

    return firstDuplicateItemToPrompt ? (
      <MyPrompt
        item={item}
        index={firstDuplicateItemIndexToPrompt}
        onAnswerSelect={this.onPromptAnswered}
      />
    ) : null;
  };

  getFirstDuplicateItemToPrompt = this.state.performUpload
    && !!this.state.items
      .find(i => i.isDuplicate && !i.shouldUploadDuplicate);

  getFirstDuplicateItemIndexToPrompt = this.state.performUpload
    && !!this.state.items
      .findIndex(i => i.isDuplicate && !i.shouldUploadDuplicate);

  onPromptAnswered = (accepted, item, index) => {
    this.setState(prevState => ({
      ...prevState,
      items: prevState.items
        .map((i, key) => (index === key ? ({
          ...item,
          shouldUploadDuplicate: accepted,
        }) : item)),
      performUpload: accepted, // if at last an item was rejected, then the upload won't be executed
    }));
  };

  uploadToApi = (items) => {
    if (!this.getFirstDuplicateItemToPrompt()) {
      const itemsToUpload = items.filter(i => i.isValid);
      uploadDataToApi(itemsToUpload);
    }
  };

  render() {
    const { items } = this.stat;
    const itemElements = items.map((item, key) => (
      <MyItem key={key} {...item} />
    ));

    const promptElement = this.getPromptElement();

    return (
      <div>
        <div style={{ display: 'flex', flexDirection: 'row' }}>
          {itemElements}
        </div>
        <Button onClick={this.onUploadButtonClick}>Upload</Button>
        {promptElement}
      </div>
    )
  }
}