Follow this tutorial get a React project deployed in Heroku, and running it your machine during development.
If you are new to GitHub, Git, Heroku, or NPM, please follow this tutorial first.
Slides available here.
First, go your GitHub webpage and create a new empty repo, get the repo's url and replace the string "https:<your_repo>.git" below. Now in your shell:
git clone https://github.com/luminaxster/swe432-heroku-react.git
cd swe432-heroku-react/
git init && git remote set-url origin "https:<your_repo>.git"
git push
Now go to your Heroku dashboard, create a new app and make these changes:
- Link your new repo, go to the app's deploy tab and choose "GitHub" deploy method, select <your_repo> from the list and enable automatic deploy.
Go to your app's settings:
-
In Config Vars, reveal them, and add
NODE_MODULES_CACHE
with valuefalse
. -
Add the following url in build packs:
https://buildpack-registry.s3.amazonaws.com/buildpacks/mars/create-react-app.tgz
Pushing changes in your repo should automatically deploy your app.
If during deployment, you logs show an error like npm ERR! Cannot read property 'match' of undefined
, you need to clean your Heroku server's cache, follow these commands:
heroku plugins:install heroku-repo
heroku repo:purge_cache -a <your_heroku_app_name>
Don't forget to replace <your_heroku_app_name>
with your app's name, now redeploy your app.
If the error shows up again, only use heroku repo:purge_cache -a <your_heroku_app_name>
.
More details here. To stop this issue from keep happening make sure step 2 changes are set in your Heroku app.
Install node.js if you haven't already, and open a shell within the 'swe432-heroku-react' folder and run the commands:
npm install
npm run start
your local app should be running at localhost:3000
.
Run npm install
every time you add packages to your package.json
, and use npm run start
every time you want to run your app locally (let this command running, it will re-run your program and refresh your browser every time you make your changes to your code).
If you get a criptic error like throw er; // Unhandled 'error' event
during npm install
, try removing the package-lock.json file and the node_modules folder, then try re-running the command. You can try these commands in your shell too (assuming you are in the project's root folder):
rm package-lock.json
rm yarn.lock
rm -rf ./node_modules
npm install
We are working with a back-end and a front-end where the front-end client via a React app is making a request to a back-end Tomcat server. The server offers a servlet as a POST microservice (doPost()) at a certain URL, to which the client fetches for via POST, and gets a response to show in the browser.
The deployed solution ia available: back-end service, and front-end client.
this workflow uses two heroku apps in different repos, this one for the the front-end and this one for the back-end. The following workflow explains how they are used together:
a.
@WebServlet(name = "EchoServlet", urlPatterns = {"/echo"})
Maps the URL yourwebsite.com/echo like in https://swe432tomcat.herokuapp.com/echo
.
b.
@Override
protected void doPost (HttpServletRequest req, HttpServletResponse res) ...
Standard servlet method for handling POST requests.
c.
out.print(new Gson().toJson(data));
Sends data
back to the client as a JSON formatted string like "{"key":"value"}"
.
d.
useEffect( ()=> {... fetchData().catch(e=>{console.log(e)});}, [url, clicks]);
This React hook will be trigger every time url
or clicks
change, including the first time they are assigned, afterwards, it will be triggered everytime the submit button is clicked.
e.
const fetchData= async()=>{...}
This is the function called within the hook, it is asynchronous so when is called it does not block the UI until it finishes its payload, instead, it returns a promise, a wrapping object that executes the function in the background and offers a method to know when it is done. This method is then(), to which you can pass a function that will be called back once the promise finishes. We did not use the then() method directly, but catch(), which works the same way but when there is an error. In d. we passed one arrow function to catch when there is an error.
f.
const res = await fetch('https://swe432tomcat.herokuapp.com/echo', {method:'POST', mode: 'cors', headers:{'Content-Type': 'application/x-www-form-urlencoded'}, body:'key1=value1&kay2=value2'}
Fetch does what HTML's form action does internally, in this case, we are configuring a call to the back-end microservice, with the same configuration as a form action, and sending the values manually in the request's body
. Previously, these values were contained within the form and sent after clicking submit.
Await is a short-hand for resolving fetch(...).then(), so it will make the call to the server(at a.) (b. will start handling it), and wait until it responds (c. finishes handling it) and comes back to the client, thus assigned to res
.
g.
const json = await res.json();
json
will be assigned the JSON data assembled in c., where res.json() returns a promise, and similarly to d., the result of then() will be assigned to the variable json, which should have the value {key1:"value1",key2:"value2"}, based on whast was sent in body
(f.).
h.
setResponse(json);
This will update the state of the variable response
to what what json
is referencing, thus, initiating a re-render the component Fetcher.
i.
const [response, setResponse] = useState(null);
This will re-run the functional Component Fetcher, now the useState hook has set response to {key1:"value1",key2:"value2"}
. Note that null
is assigned only the first time the compenent is executed.
j.
response?JSON.stringify(response):...
Since response has {key1:"value1",key2:"value2"}
, response?
evaluate to true
, thus printing the following {"key1":"value1","key2":"value2"}
in the browser.
React you can do conditional rendering, that is, depending on your app's state choose to show things or not. e.g.:
function Component(){.... return condition?<ComponentA/>: <ComponentB/>;}// you can return null, too.
Similarly to step j, you can use response or another variable to keep track if the user clicks the submit button and conditionally render components, for example, a form or result list. These are the relevant statements:
function YourComponent(){
...
const [isSubmitted, setIsSubmitted] = useState(false);
....
return isSubmitted? <ResultComponent ... />:
<FormComponent...>
...
<button onClick={()=>{fetch(...)... setIsSubmitted(true)}}>submit</button>
</FormComponent>;
}
Creating a React app for Hook from scratch