0

I have a fetch call function that handles with a BackEnd. This call takes some time (couple of seconds).

There is also a table class that expect the data from the BackEnd to fill some of the rows. I am passing the function that does the fetch call via props.This function returns a list of values which the table class needs in order to fill some of the rows. Though, because the fetch call takes some time it seems like the value of the list in the table class side is always 'undefined' and some of the table rows that expect those values stays empty after the much quicker render method doing it's job..

I am using the "material-ui" npm package in order to build the table.

This is the fetch function:

var reuslts = [];


async callBackEnd() {
    await fetch('*******', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: JSON.stringify({
        request: {
          Purpose: "Ring",
          price: "2000",
          shape: "Round"
        }
      })
    })
      .then(r => r.json())
      .then(r => {
        r.forEach(function (element) {
          reuslts.push('NO: ' + element);
        });
      });
 console.log("I am near the end of app.js and the first value of the list is " + diamondReuslts[0]);

        return reuslts;

      }

This is where I pass the function as props:

  render() {
    const { title } = this.context;
    return (
      <div className="center-screen">
        {title} <Table intents={intents} callBackEnd={this.callBackEnd()} />
      </div>
    );
  }

This is the render method of the table class :

const SimpleTable = (props) => {
    const { classes } = props;
    intents1 = props.intents;
    reuslts1 = props.callBackEnd;

    console.log("I am at the beginning of Table.js and first value of the list is " + diamondReuslts1[0]);//Returns undefined


    return (
        <Paper className={classes.root}>
            <Table className={classes.table}>
                <TableHead>
                    <TableRow>
                        <CustomTableCell>Image</CustomTableCell>
                        <CustomTableCell align="right">Price</CustomTableCell>
                        <CustomTableCell align="right">Id reference</CustomTableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    {rows.map(row => (

                        <TableRow key={row.id}>
                            <CustomTableCell component="th" scope="row">
                                <img src={row.feature} height="42" width="42"></img>
                            </CustomTableCell>
                            <CustomTableCell align="left">{intents1[row.id]}</CustomTableCell>
                            <CustomTableCell align="left">{reuslts1[row.id]}</CustomTableCell>

                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </Paper>
    );

}

In this class the "results1" variable is undefined. When longing the returned value of Results from the callBackEnd function the value and message returned only long after the log of the table class returns "undefined".

What can I do in order to make the part of "rendering the table" wait or rerender when the list is returned from the BackEnd ?

Ben J
  • 147
  • 2
  • 14
  • 1
    `callBackEnd={this.callBackEnd()}` You are invoking the function here... To pass a reference as a prop do not invoke it `callBackEnd={this.callBackEnd}` Also, look into `componentDidMount` - all ajax calls should be in that lifecycle method... – SakoBu Mar 26 '19 at 18:53

2 Answers2

1
class YourComponent extends React.Component {
    constructor(props) {
        super(props)
        state = {
           result: [],
        };
        this._fetch();
    }

    callBackEnd(/** **/)

    async _fetch() {
        let result = await this.callBackEnd();
        this.setState({result});
    }

    render() {
        const { title } = this.context;
        return (
            <div className="center-screen">
                {title} <Table intents={intents} result={this.state.result} />
            </div>
        );
    }
}
CraftyMonkey
  • 395
  • 1
  • 6
  • Thank you, it worked. Can you explain why passing the state instead , solved the problem ? – Ben J Mar 26 '19 at 19:01
  • Hey, it is not working because you re passing state. I just gave you a good way of doing it, but you could also have done somehting like: render() { let data = this.callbackEnd(); return (
    ) } I might be wrong but the way that you are doing it is not bad, your function is just not called at the right moment. I invite you to learn more about lifecycle and render process.
    – CraftyMonkey Mar 27 '19 at 13:14
1

There's a lengthy post about why @CraftyMonkey's solution works. It boils down to:

  • Using the constructor to initialize state.
  • Using an Async/Await pattern to give fetch time to finish.
  • Saving the results, once available, to state for use in your component.

These two articles are helpful if you just want to jump to the implementation details:

It'sNotMe
  • 1,184
  • 1
  • 9
  • 29