A fantastic example is create-react-app, by Dan (Redux) Abramov et al, which makes it ludicrously-simple to get going with a React.JS front-end application that incorporates all of the current best-practices and configuration. I came across a very fine article that discussed hosting a create-react-app front-end on a Ruby-on-Rails server (in turn on Heroku) and figured it would be a good exercise to do a version with the Play Framework 2.5 (Scala) on the back end. This version will have a lot fewer animated GIFs and general hilarity, but hopefully it is still a worthwhile exercise!
I won't go into setting up a simple Play app as complete instructions for both beginners and experts are provided at the Play website, and it can be as simple as typing:
% sbt new % sbt runOnce you've got your Play app all happy, getting the front-end going is as simple as running two commands in your project's root directory:
% npm install -g create-react-app % create-react-app clientto create a React application in the client directory. You can check it works with:
% cd client % npm startGreat. Now's a good time to commit your new client directory to Git, although you'll definitely want to add client/node_modules to your .gitignore file first. Let's modify the backend to have a tiny little JSON endpoint, call it from React when the app mounts, and display the content. First, we just add one line to our package.json so that backend data requests get proxied through the front-end server, making everything just work with no CORS concerns:
"private": true, "proxy": "http://localhost:9000/", "dependencies": { "react": "^15.5.4", "react-dom": "^15.5.4" },Make sure you kill-and-restart your React app after adding that line. Next, let's whip up a Play endpoint that returns some JSON: In conf/routes:
GET /dummy-json controllers.DummyController.dummyJsonIn app/controllers/DummyController.scala:
class DummyController extends Controller { val logger = Logger("DummyController") def dummyJson = Action { logger.info("Handling request for dummy JSON") Ok(Json.obj( "foo" -> "foo", "bar" -> "bar", "bazzes" -> Seq("baz1", "baz2", "baz3") ) ) }Check that's all good by hitting http://localhost:9000/dummy-json directly with your browser. Now we put our front-end hat on and get the React app to fetch the JSON when it mounts:
class App extends Component { componentDidMount() { console.log('Mounted'); fetch('/dummy-json',{ accept: 'application/json'}) .then(response => response.json() ) .then(json => console.log(json) ) .catch(error => console.error(error)); } ... }Setting the accept header is not strictly necessary but it helps create-react-app to know that this request should be proxied. Plus it's generally good form too. Now when your app hot-reloads, watch your browser's Network tab. You'll see the request go out on port 3000, the server log the request and respond on 9000, and the response arrive back on port 3000. Let's finish off the local-development part of this little demo by wiring that response into our app's state so that we can render appropriately:
class App extends Component {
constructor() {
super();
this.state = {};
}
componentDidMount() {
console.log('Mounted');
this.fetch('/dummy-json').then( result => {
this.setState({
result
});
});
}
fetch (endpoint) {
return new Promise((resolve, reject) => {
window.fetch(endpoint, { accept: 'application/json'})
.then(response => response.json())
.then(json => resolve(json))
.catch(error => reject(error))
})
}
render() {
let { result } = this.state;
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
{result ? (
<h3>{result.foo}</h3>
) : (
<h3>Loading...</h3>
)
}
</div>
<p className="App-intro">
To get started, edit src/App.js
and save to reload.
</p>
</div>
);
}
}
So easy!
In the next installment, we'll consider deployment to Heroku.
No comments:
Post a Comment
Comments welcome - spam is not. Spam will be detected, deleted and the source IP blocked.