I'm working on a simple food ordering app through Dot Net 6 and React.
I have a table for the list of foods, which contains all the details for that specific food, including the Restaurant which offers it.
In the table, I want to show the restaurant's name instead of its Id which is coming from the food data. list of foods
My intrinsic approach was to use this function
function getRestaurantById(id: number) {
let res = '';
axios.get('https://localhost:7005/api/Restaurant/' id).then(response => {
res = response.data.name;
});
return res;
}
When console logging inside the .then()
method of axios, I get the restaurant name I want. However, the res
variable gets saved as undefined. How do I work around this? I want this function to return a string value.
Note: I can't (or maybe could but don't know how to) use a useState function since I will be calling this function within the table data.
{foods.map(food => (
<tr key={food.foodId}>
<td>{food.foodId}</td>
<td>{food.name}</td>
<td>{food.ingredients}</td>
<td>{food.price}</td>
<td>{food.cuisineType}</td>
<td>{getRestaurantById(food.restaurant)}</td>
<td><Button className='btn' onClick={() => {setFood(food); handleFormOpen()}}>Edit</Button></td>
<td><Button className='btn action' onClick={() => deleteFood(food.foodId.toString())}>Delete</Button></td>
</tr>
))}
CodePudding user response:
Your issue is with the asynchronous nature of axios.get(). When the request is run it runs on a separate thread to the function and therefore changes the order of operations. We will call the main thread thread 1.x and the HTTP request thread 2.x. Each x is the operation that we perform on the thread.
function getRestaurantById(id: number) {
// (1.1) We set res to an empty string.
let res = '';
// (1.2 -> 2.1) The request is called in thread 1 and started on thread 2.
axios.get('https://localhost:7005/api/Restaurant/' id).then(response => {
// (2.2) We set the value of the reference to 'res' to response.data.name
res = response.data.name;
});
// (1.3) return res
return res;
}
Now the operations in the following order:
1.1 -> 1.2 -> 1.3
|
-> 2.1 -> 2.2
Because of the timing it takes to make perform the tasks on thread 2 the res variable won't have been changed by the time 1.3 is ran.
Now you might think that since the reference is changed it would update in React, but it won't because changing a reference on it's own does not cause a rerender.
Here is a component written to fit your needs (or if not it should help you to see where to go):
import React, {useState, useEffect} from 'react';
import axios from 'axios';
const foods = [
{
foodId: 1,
name: 'Pizza',
ingredients: 'Mozzarella, wheat, tomato, etc',
price: 69,
cuisineType: 'Italian',
restaurant: 2
}
]
function Foods(food, handleDelete, handleEdit) {
const [restaurant, setRestaurant] = useState(null);
useEffect(() => {
// With axios
axios.get('https://localhost:7005/api/Restaurant/' food.restaurant).then(response => setRestaurant(response.data));
// With fetch
fetch('https://localhost:7005/api/Restaurant/' food.restaurant, {method: 'GET'})
.then(r => r.json())
.then(json => setRestaurant(json))
}, [food.restaurant]);
return (
<tr>
<td>{food.foodId}</td>
<td>{food.name}</td>
<td>{food.ingredients}</td>
<td>{food.price}</td>
<td>{food.cuisineType}</td>
<td>{restaurant?.name}</td>
<td><Button className='btn' onClick={() => handleEdit(food)}>Edit</Button></td>
<td><Button className='btn action' onClick={() => handleDelete(food.foodId.toString())}>Delete</Button></td>
</tr>
)
}
export default function Foo() {
return foods.map(food => (<Foods foods={food} handleEdit={(food) => console.log('Tried to edit: ', food)} handleDelete={(food) => console.log('Tried to delete: ', food)}/>))
}
CodePudding user response:
I would suggest you to get the restaurant name with the list of foods from the backend itself because right now for every food you are calling a get request. So if there are 1000's of foods, you will be calling 1000 request for every food which is not a good idea.