Suresh Rohan's Blog

This blog is all about Java, J2EE,Spring, Angular, React JS, NoSQL, Microservices, DevOps, BigData, Tutorials, Tips, Best practice, Interview questions, Views, News, Articles, Techniques, Code Samples, Reference Application and much more

Thursday, January 25, 2018

Create a Project with Create React App

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.
To create a React project, make sure you have Node.jsCreate React App, and Yarn installed.
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.
Welcome to React
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.
Beer List in Angular

Create a BeerList Component

To make this application easier to maintain, move the beer list fetching and rendering from App.tsx to its own BeerListcomponent. 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.
Beer list with Giphy 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