Create a Project with Create React App
Creating an API seems to be the easy part these days, thanks in large part to Spring Boot. In this section, I hope to show you that creating a UI with React is pretty easy too. If you follow the steps below, you’ll create a new React app, fetch beer names and images from APIs, and create components to display the data.
npm install -g create-react-app@1.4.3
From a terminal window, cd into the root of the
spring-boot-react-example
directory and run the following command. This command will create a new React application with TypeScript support.create-react-app client --scripts-version=react-scripts-ts
After this process runs, you will have a new
client
directory with all the necessary dependencies installed. To verify everything works, cd into the client
directory and run yarn start
. If everything works, you should see the following in your browser.
Thus far, you’ve created a
good-beers
API and a React app, but you haven’t created the UI to display the list of beers from your API. To do this, open client/src/App.tsx
and add a componentDidMount()
method.componentDidMount() {
this.setState({isLoading: true});
fetch('http://localhost:8080/good-beers')
.then(response => response.json())
.then(data => this.setState({beers: data, isLoading: false}));
}
React’s component lifecycle will call the
componentDidMount()
method. The code above uses fetch
, a modern replacement for XMLHttpRequest
. It’s supported in most browsers according to caniuse.com.
You can see that it sets the
beers
state with the response data. To initialize the state for this component, you need to override the constructor.constructor(props: any) {
super(props);
this.state = {
beers: [],
isLoading: false
};
}
For this to work, you need to add parameter types to the class signature. The code below shows what the top of your
App
class should look like at this point.class App extends React.Component<{}, any> {
constructor(props: any) {
super(props);
this.state = {
beers: [],
isLoading: false
};
}
// componentDidMount() and render()
}
Change the
render()
method to have the following JSX. JSX is Facebook’s XML-like syntax that renders HTML via JavaScript.render() {
const {beers, isLoading} = this.state;
if (isLoading) {
return <p>Loading...</p>;
}
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<div>
<h2>Beer List</h2>
{beers.map((beer: any) =>
<div key={beer.id}>
{beer.name}
</div>
)}
</div>
</div>
);
}
If you look at
http://localhost:3000
in your browser, you’ll see a “Loading…” message. If you look in your browser’s console, you’ll likely see an issue about CORS.Failed to load http://localhost:8080/good-beers: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
To fix this issue, you’ll need to configure Spring Boot to allow cross-domain access from
http://localhost:3000
.Configure CORS for Spring Boot
In the server project, open
server/src/main/java/com/example/demo/beer/BeerController.java
and add a @CrossOrigin
annotation to enable cross-origin resource sharing (CORS) from the client (http://localhost:3000
).import org.springframework.web.bind.annotation.CrossOrigin;
...
@GetMapping("/good-beers")
@CrossOrigin(origins = "http://localhost:3000")
public Collection<Beer> goodBeers() {
After making these changes, restart the server, refresh your browser, and you should be able to see a list of beers from your Spring Boot API.

Create a BeerList Component
To make this application easier to maintain, move the beer list fetching and rendering from
App.tsx
to its own BeerList
component. Create src/BeerList.tsx
and populate it with the code from App.tsx
.import * as React from 'react';
class BeerList extends React.Component<{}, any> {
constructor(props: any) {
super(props);
this.state = {
beers: [],
isLoading: false
};
}
componentDidMount() {
this.setState({isLoading: true});
fetch('http://localhost:8080/good-beers')
.then(response => response.json())
.then(data => this.setState({beers: data, isLoading: false}));
}
render() {
const {beers, isLoading} = this.state;
if (isLoading) {
return <p>Loading...</p>;
}
return (
<div>
<h2>Beer List</h2>
{beers.map((beer: any) =>
<div key={beer.id}>
{beer.name}
</div>
)}
</div>
);
}
}
export default BeerList;
Then change
client/src/App.tsx
so it only contains a shell and a reference to <BeerList/>
.import * as React from 'react';
import './App.css';
import BeerList from './BeerList';
const logo = require('./logo.svg');
class App extends React.Component<{}, any> {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<h2>Welcome to React</h2>
</div>
<BeerList/>
</div>
);
}
}
export default App;
Create a GiphyImage Component
To make it look a little better, add a GIPHY component to fetch images based on the beer’s name. Create
client/src/GiphyImage.tsx
and place the following code inside it.import * as React from 'react';
interface GiphyImageProps {
name: string;
}
class GiphyImage extends React.Component<GiphyImageProps, any> {
constructor(props: GiphyImageProps) {
super(props);
this.state = {
giphyUrl: '',
isLoading: false
};
}
componentDidMount() {
const giphyApi = '//api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=';
fetch(giphyApi + this.props.name)
.then(response => response.json())
.then(response => {
if (response.data.length > 0) {
this.setState({giphyUrl: response.data[0].images.original.url});
} else {
// dancing cat for no images found
this.setState({giphyUrl: '//media.giphy.com/media/YaOxRsmrv9IeA/giphy.gif'});
}
this.setState({isLoading: false});
});
}
render() {
const {giphyUrl, isLoading} = this.state;
if (isLoading) {
return <p>Loading image...</p>;
}
return (
<img src={giphyUrl} alt={this.props.name} width="200"/>
);
}
}
export default GiphyImage;
Change the
render()
method in BeerList.tsx
to use this component.import GiphyImage from './GiphyImage';
...
render() {
const {beers, isLoading} = this.state;
if (isLoading) {
return <p>Loading...</p>;
}
return (
<div>
<h2>Beer List</h2>
{beers.map((beer: any) =>
<div key={beer.id}>
{beer.name}<br/>
<GiphyImage name={beer.name}/>
</div>
)}
</div>
);
}
The result should look something like the following list of beer names with images.

You’ve just created a React app that talks to a Spring Boot API using cross-domain requests. Congratulations!
No comments:
Post a Comment