A React app runs in the visitor’s browser. To exchange data between visitors, a database is required. For security reasons, it should be avoided to establish a direct connection to the database as this can be seen in the browser. Typically, communication with a database is handled through a backend.
Example of Fetching Data from the Backend Using HTTP Requests
useEffect(() => {
fetch('http://localhost:3000/places').then((response) => {
return response.json()
}).then((resData) => {
setAvailablePlaces(resData.places);
});
}, []);
useEffect(() => {
async function fetchPlaces() {
const response = await fetch('http://localhost:3000/places');
const resData = await response.json();
setAvailablePlaces(resData.places);
}
fetchPlaces();
}, []);
Loading Message
//AvailablePlaces.jsx
import { useEffect, useState } from 'react';
import Places from './Places.jsx';
export default function AvailablePlaces({ onSelectPlace }) {
const [isFetching, setIsFetching] = useState(false);
const [availablePlaces, setAvailablePlaces] = useState([]);
useEffect(() => {
async function fetchPlaces() {
setIsFetching(true);
const response = await fetch('http://localhost:3000/places');
const resData = await response.json();
setAvailablePlaces(resData.places);
setIsFetching(false);
}
fetchPlaces();
}, []);
return (
<Places
title="Available Places"
places={availablePlaces}
isLoading={isFetching}
loadingText="Fetching place data..."
fallbackText="No places available."
onSelectPlace={onSelectPlace}
/>
);
}
//Places.jsx
export default function Places({ title, places, fallbackText, onSelectPlace, isLoading, loadingText
}) {
console.log(places);
return (
<section className="places-category">
<h2>{title}</h2>
{isLoading && <p className="fallback-text">{loadingText}</p>}
{!isLoading && places.length === 0 && <p className="fallback-text">{fallbackText}</p>}
{!isLoading && places.length > 0 && (
<ul className="places">
{places.map((place) => (
<li key={place.id} className="place-item">
<button onClick={() => onSelectPlace(place)}>
<img src={`http://localhost:3000/${place.image.src}`} alt={place.image.alt} />
<h3>{place.title}</h3>
</button>
</li>
))}
</ul>
)}
</section>
);
}
Handling Errors When Sending/Receiving Data via HTTP
//AvailablePlaces.jsx
import { useEffect, useState } from 'react';
import Places from './Places.jsx';
import Error from './Error.jsx';
export default function AvailablePlaces({ onSelectPlace }) {
const [isFetching, setIsFetching] = useState(false);
const [availablePlaces, setAvailablePlaces] = useState([]);
const [error, setError] = useState();
useEffect(() => {
async function fetchPlaces() {
setIsFetching(true);
try {
const response = await fetch('http://localhost:3000/places1');
const resData = await response.json();
// 200, 300 => ok
// 400, 500 => not ok
if (!response.ok) {
throw new Error('Failed to fetch places');
}
setAvailablePlaces(resData.places);
} catch (error) {
setError({message: error.message || 'Could not fetch places, please try again later'});
}
setIsFetching(false);
}
fetchPlaces();
}, []);
if (error) {
return <Error title="An error occurred!" message={error.message}/>
}
return (
<Places
title="Available Places"
places={availablePlaces}
isLoading={isFetching}
loadingText="Fetching place data..."
fallbackText="No places available."
onSelectPlace={onSelectPlace}
/>
);
}
//Error.jsx
export default function Error({ title, message, onConfirm }) {
return (
<div className="error">
<h2>{title}</h2>
<p>{message}</p>
{onConfirm && (
<div id="confirmation-actions">
<button onClick={onConfirm} className="button">
Okay
</button>
</div>
)}
</div>
);
}