简体   繁体   中英

Render a list of objects on a table object using axios calls : Rendering logic

My component is supposed to render a list of driver in a table where it puts the diver name in the first column and the tractor number on the second column of each row. However, if the reference to the driver table returns a null then it returns instead of the tractor number. <td><Link to ={`/assign-tractor/${driver.id}`}> Assign Tractor </Link></li>)</td>

how do I achieve this?

Below is what my what my code looks like in the back end and front end.

I have a user table with has references to a tractor table

Model:

public class User{
       @Id
       @GeneratedValue(strategy = GenerationType.AUTO)
       @Column(name ="user_id")
       private Long id;

       @OneToOne
       @JoinColumn(name = "tractor_id", referencedColumnName = "tractor_id")
       private Tractor tractor;

       @OneToOne
       @JoinColumn(name = "order_id", referencedColumnName = "order_id")
       private Order order;
} 


@Entity
@Table(name = "`order`")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="order_id")
    private Long id;

    private String orderNumber;
    }

@Entity
@Table(name = "tractor")
public class Tractor {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name ="tractor_id")
    private Long id;

    private String tractorNumber;

}

Controller:

       @CrossOrigin
       @RestController
       @RequestMapping("/user")
       public class UserController {
              private UserService userService;

          @Autowired
          public UserController(UserService userService) {
             super();
             this.userService = userService;
              }

              @GetMapping("drivers")
          public List<User> allDrivers() {
             return userService.findAllDriver();
          }

              @GetMapping("/id/{id}")
          public User getUserByUserId(@PathVariable long id) {
             return userService.findUserById(id);
          }

      }

      @CrossOrigin
      @RestController
      @RequestMapping("/tractor")
      public class TractorController {

         private TractorService tractorService;

         public TractorController(TractorService tractorService) {
            super();
            this.tractorService = tractorService;
         }

             @GetMapping("/tractors/{id}")
         public Tractor getTractorById(@PathVariable Long id) {
            return tractorService.FindTractorById(id).get();
         }
      }

React FrontEnd


import React, { useState,Component} from 'react'
import axios from 'axios'
import './assignment-table.css';
import {withRouter} from 'react-router-dom';

class AssignmentTable extends Component {

  constructor(props){
    super(props);
    this.state = {
      drivers : [],
      isLoggeIn: false,
      tractorNumber: ""
    }
  }
  componentDidMount(){
    axios.get(`http://localhost:3000/project/user/users`)
    .then(response => {
      console.log(response)
      this.setState({ drivers: response.data })
    })
    .catch( err => console.log(err))

//get tractor using user tractor reference, then assigns tractor number
/*  
axios.get(`http://localhost:3000/project/tractor/${users.tractor}`)
  .then(response => {
    console.log(response)
    this.setState({ tractorNumber: response.data.tractorNumber })
  })
  .catch( err => console.log(err))
}
*/
  render() {
    let drivers = this.state.drivers ? this.state.drivers.map((item, key) => {
   return (
   <tr>
   <td key = {key}>{item.firstName}  {item.lastName}</td>
    </tr>)
    }) : "No Avilable Driver"

    return (
            <table class="table">
              <thead>
                 <tr>
                  <th scope="col">Driver Name</th>
                  <th scope="col">Tractor</th>
                  <th scope="col">Order</th>
                </tr>
              </thead>

              <tbody>
                {drivers}
             </tbody>
             </table>
);
}
  }
export default withRouter (AssignmentTable);

the hard coded bootstrap version of what I am trying to replicate:


<!DOCTYPE html>
<html lang="en">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>

  <table class="table">
    <thead>
      <tr>
        <th scope="col">#</th>
        <th scope="col">Driver Name</th>
        <th scope="col">Tractor Number</th>
        <th scope="col">Order Number</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">1</th>
        <td>Mark Otto</td>
        <td>123215</td>
        <td>89562</td>
      </tr>
      <tr>
        <th scope="row">2</th>
        <td>Jacob Thornton</td>
        <td>123254</td>
        <td>564232</td>
      </tr>
      <tr>
        <th scope="row">3</th>
        <td>Larry the Bird</td>
        <td><a href="#">Assign Tractor</a></td>
        <td><a href="#">Assign Order</a></td>
      </tr>
    </tbody>
  </table>
</body>
</html>

So there are several ways to solve this.

The "naive" way would be to make a request to your backend for each record, and then when that record comes back, display the information. However, that is potentially a lot of "slow" requests, and browsers limit the number of requests that you can do at once. This type of query is called n+1 ( 2n+1 in your case) and is generally considered a bad thing from a performance perspective. But it can be done, and it is fairly easy.

The alternative to that would be to return the information needed to the front end with just one query. So you would change your backend to include tractorNumber and orderNumber in the information provided by the user/users endpoint. This makes the front end dead simple.

I would also consider actually using tractorNumber and orderNumber as the actual id's, if:

  • they were unique
  • they existed when the record was created and never changed

Be careful that you don't just move your n+1 query to the backend, because then the slowness moves there (but it would probably still be faster). When you lookup the information for the user endpoint, make sure that it is joining the order and tractor information as well, so that it is available without another lookup.

The reason you may not want to change your backend is that it kinda makes the data less "pure", as now your endpoints are returning data for multiple models at once. And sometimes you just can't change the backend for whatever reason. You might also decide that this view deserves it's own special endpoint.

Another solution that might work is getting a list of all tractor and order information on the front end and storing that so that you can look at that cached information instead of asking the backend each time. But the downside of that is that you have to store all the tractor and order info, and that could be a lot. If you decide to "paginate" your backend results, then this solution becomes especially difficult, but might work.

If you do decide to go with the "naive" approach, I'd recommend extracting each row into a component and then have it have it's own lookup, for example:

 function getDrivers(){ // this would be the response in the real case /** return axios.get(`http://localhost:3000/project/user/users`).then(response => { console.log('drivers request:', response) return response.data }) */ // fake response for example return new Promise((resolve) => setTimeout(() => resolve([ {id: 11, firstName: "Dell", lastName: "McDonald", tractor_id: 123, order_id: 234}, {id: 12, firstName: "Bob", lastName: "Crockett", tractor_id: 145, order_id: 245}, {id: 13, firstName: "Joe", lastName: "Fresh", tractor_id: null, order_id: null} ]), 1000)) } function getTractorNumber(tractor_id) { //get tractor using user tractor reference, then assigns tractor number /* axios.get(`http://localhost:3000/project/tractor/${tractor_id}`).then(response => { console.log("tractor request:", response) return response.data.tractorNumber }) */ //Fake tractor response return new Promise((resolve) => setTimeout(() => resolve(`#T${tractor_id}-00`), Math.random()*5000)) } function getOrderNumber(order_id) { /* axios.get(`http://localhost:3000/project/order/${order_id}`).then(response => { console.log("order request:", response) return response.data.tractorNumber }) */ //Fake order response return new Promise((resolve) => setTimeout(() => resolve(`#O${order_id}`), Math.random()*5000)) } class AssignmentTable extends React.Component { constructor(props){ super(props); this.state = { drivers: [] } } componentDidMount(){ getDrivers().then(drivers => { this.setState({ drivers }) }).catch( err => console.log(err)) } render() { let drivers = this.state.drivers.length? this.state.drivers.map((item) => { return (<DriverRow driver={item} key={item.id} />) }): "No Available Drivers" return ( <table class="table"> <thead> <tr> <th scope="col">Driver Name</th> <th scope="col">Tractor</th> <th scope="col">Order</th> </tr> </thead> <tbody> {drivers} </tbody> </table> ); } } class DriverRow extends React.Component { constructor(...args) { super(...args) this.state = { tractorNumber: "loading...", orderNumber: "loading..." } } componentDidMount(){ getTractorNumber(this.props.driver.tractor_id).then(tractorNumber => { this.setState({ tractorNumber }) }).catch( err => console.log(err)) getOrderNumber(this.props.driver.order_id).then(orderNumber => { this.setState({ orderNumber }) }).catch( err => console.log(err)) } render() { const {firstName, lastName, tractor_id, order_id} = this.props.driver return (<tr> <td>{firstName} {lastName}</td> <td>{tractor_id? this.state.tractorNumber: <a href="#">Assign Tractor</a>}</td> <td>{order_id? this.state.orderNumber: <a href="#">Assign Order</a>}</td> </tr>) } } ReactDOM.render(<AssignmentTable />, document.getElementById('main'))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <main id="main"/>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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