简体   繁体   English

在reactjs中递归调用函数

[英]Calling a function recursively in reactjs

I am calling an API recursively using axios in React.我在 React 中使用 axios 递归调用 API。 But it is not giving desired result.但它没有给出预期的结果。 Here is the snippet:这是片段:

callApi =(index)=> {
         console.log('index data : ',this.props.modalData[index]);
        let modalData = this.props.modalData
         if(index < this.props.modalData.length-1){
            const options ={
                method: 'post',
                data: this.props.modalData[index],
                json: true,
                url: config.api.root+'/sp/tms/tgbTripRefUpdate',
                "headers": {'Authorization': localStorage.getItem('authHeader')},
            }
            axios(options).then(res=>{
                modalData[index].Status='Success';
                let display = [...this.state.displayModalData]
                display.push(modalData[index])
                this.setState({
                    "displayModalData" : [...display],
                    change : !this.state.change
                },this.callApi(index+1))  
            }).catch(err=>{
                modalData[index].Status='Failure';
                let display = [...this.state.displayModalData]
                display.push(modalData[index])
                this.setState({
                    "displayModalData" : [...display],
                    change : !this.state.change
                },this.callApi(index+1))                
            });
         }
         else{
            this.setState({
                showCloseBtn: true
            })
         }
     }

The function callApi gets called many times.函数callApi被多次调用。 Unable to understand why.无法理解为什么。

You're using the callback of setState in a bad way, it should be () => this.callApi(index + 1) :您正在以一种糟糕的方式使用setState的回调,它应该是() => this.callApi(index + 1)

callApi = index => {
  if(index > this.props.modalData.length) return;
  console.log("index data : ", this.props.modalData[index]);
  let modalData = this.props.modalData;
  if (index < this.props.modalData.length - 1) {
    const options = {
      method: "post",
      data: this.props.modalData[index],
      json: true,
      url: config.api.root + "/sp/tms/tgbTripRefUpdate",
      headers: { Authorization: localStorage.getItem("authHeader") }
    };
    axios(options)
      .then(res => {
        modalData[index].Status = "Success";
        let display = [...this.state.displayModalData];
        display.push(modalData[index]);
        this.setState(
          {
            displayModalData: [...display],
            change: !this.state.change
          },
          () => this.callApi(index + 1) // here
        );
      })
      .catch(err => {
        modalData[index].Status = "Failure";
        let display = [...this.state.displayModalData];
        display.push(modalData[index]);
        this.setState(
          {
            displayModalData: [...display],
            change: !this.state.change
          },
          () => this.callApi(index + 1) // here
        );
      });
  } else {
    this.setState({
      showCloseBtn: true
    });
  }
};

Here are two approaches:这里有两种方法:

  • The loops occurs within a single call and if one fails then the next won't execute -- recommended, especially if the data is related and reliant upon each other to be in sync and update-to-date循环发生在一次调用中,如果一个调用失败,则下一个将不会执行——推荐,特别是如果数据相关并且相互依赖以保持同步和更新到最新
  • Handle the exception and move on the next call -- recommended if the data is independent from each other处理异常并继续下一次调用——如果数据彼此独立,建议使用

Working example (this example includes GET requests to a public API, but just swap in your POST requests; in addition, this example includes both options above this.fetchData (option 1) and this.fetchDataRecursively (option 2)):工作示例(此示例包括对公共 API 的GET请求,但只是交换您的POST请求;此外,此示例包括this.fetchData (选项 1)和this.fetchDataRecursively (选项 2)之上的两个选项):

编辑华丽拥抱 2w1p2


App.js应用程序.js

import React, { Component } from "react";
import app from "./axiosConfig";
import Spinner from "./Spinner";
import "./styles.css";

class App extends Component {
  state = {
    data: [],
    showCloseButton: false,
    isLoading: true,
    err: ""
  };

  /* executed when component is mounted */
  componentDidMount() {
    // this.fetchData();
    this.fetchDataRecursively();
  }

  /* loops through modalData as one unit and retrieves data */
  fetchData = async () => {
    const { modalData } = this.props;
    try {
      for (const index of modalData) {
        /* 
          using a custom axios config found in "./axiosConfig" 
          to fetch data by modalData index with a predefined "baseURL" 
        */
        const res = await app.get(`/${index}`);

        /* 
          the code below is not needed;
          it's just there to add some time between each call 
        */
        await new Promise(res => setTimeout(() => res(), 1000));

        /* using setState function to get prevState and adding new data to it */
        this.setState(prevState => ({
          data: [...prevState.data, res.data]
        }));
      }
      /* once the loop above is done, sets isLoading to false */
      this.setState({ isLoading: false });
    } catch (err) {
      /* if any of the fetches fail, the next calls won't be executed and the error is set to state */
      this.setState({ err, isLoading: false });
    }
  };

  /* rescursively loops failData as individual units and retrieves data */
  fetchDataRecursively = async (index = 0) => {
    const { failData } = this.props;
    if (failData[index] !== undefined) {
      try {
        /* 
            using a custom axios config found in "./axiosConfig" 
            to fetch data by modalData index with a predefined "baseURL" 
          */
        const res = await app.get(`/${failData[index]}`);

        /* 
            the code below is not needed;
            it's just there to add some time between each call 
          */
        await new Promise(res => setTimeout(() => res(), 1000));

        /* using setState function to get prevState and adding new data to it */
        this.setState(prevState => ({
          data: [...prevState.data, res.data]
        }));

        this.fetchDataRecursively(index + 1);
      } catch (err) {
        /* if any of the fetches fail, add to state */
        this.setState(
          prevState => ({
            data: [
              ...prevState.data,
              { index: `Failed to fetch ${index}`, reason: err }
            ]
          }),
          () => this.fetchDataRecursively(index + 1)
        );
      }
    } else {
      this.setState({ isLoading: false });
    }
  };

  /* mimics a modal toggle */
  toggleModalButton = () =>
    this.setState(prevState => ({
      showCloseButton: !prevState.showCloseButton
    }));

  /*
    The below is rendered according to state:
    If "isLoading" is true: show Spinner
    if "isLoading" is false but "err" is true: show err
    Else: initially show the data and a button
         data button toggles the data visibility
  */
  render = () => (
    <div style={{ width: 600, margin: "0 auto" }}>
      {this.state.isLoading ? (
        <Spinner />
      ) : this.state.err ? (
        <p>{this.state.error}</p>
      ) : (
        <div>
          {!this.state.showCloseButton && (
            <pre
              style={{
                background: "#ebebeb",
                overflowY: "auto"
              }}
            >
              <code>{JSON.stringify(this.state.data, null, 4)}</code>
            </pre>
          )}
          <button onClick={this.toggleModalButton}>
            {!this.state.showCloseButton ? "Close" : "Open"} Modal
          </button>
        </div>
      )}
    </div>
  );
}

export default App;

axiosConfig.js axiosConfig.js

import get from "lodash.get";
import axios from "axios";

// const { baseURL } = process.env;
const baseURL = "https://jsonplaceholder.typicode.com/todos";

const app = axios.create({
  baseURL
});

app.interceptors.response.use(
  response => response,
  error => {
    const err = get(error, ["response", "data", "err"]);

    return Promise.reject(err || error.message);
  }
);

export default app;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM