This project is designed to give you an opportunity to build something from scratch and to teach you how to connect all the pieces of an application together. All of the instructions give you an idea of what order to do things in, but there won't be any guidance or solutions on how to write the code itself. The styling of the project is not included in the instructions at all and should be completed at your discretion.
This project is broken into two parts. The setup instructions are more detailed and are designed to get you started. The parts have varying levels of detail, with the newer concepts explained more. This gives you a chance to practice your skills on your own. Your mentors have also been asked to provide only minimal guidance. They can point you in the right direction, but cannot help you code. This project is a chance for you to combine and showcase the skills you've learned so far.
Good luck and work hard!
This section will help you create the files and install the packages you need.
Do NOT clone this repository. You will be creating your own.
- Navigate in the terminal to the folder you would like to store your simulation in, and then run
create-react-app houser
.cd
into the folder to get started. create-react-app
starts your new project out as a local git repository, so you don't have to set that up. You can start using git commands right away.- Open up your Github profile page and click on 'Repositories'.
- Click on the 'New' button. Name your repo (we suggest 'houser', the same name as your local folder). Do NOT initialize the repo with a README.
- Now go back to your terminal and run
git remote add origin [INSERT-GITHUB-URL-HERE]
with the url from the remote repo you just created. - And finally, run
git push origin master -u
to push your local files to your remote repo for the first time.
Make sure to commit and push your code often. It's not fun to lose hours of work.
You have already created a React application as part of setting up the Github repo, so now you will start adding packages and files to that project.
- Run
npm i axios react-router-dom
- Create a
Components
folder inside ofsrc
. - Inside your
Components
folder create a folder for each component you will be using (Dashboard, House, Wizard, and Header) - Inside each of these folders create a Javascript file named the same thing. Make sure to capitalize the first letter!
- Create a simple class component in the Dashboard, Wizard, Header, and House files. For now just return a div containing the component's name from the render method.
- Now render the Dashboard, Wizard, and Header components in App.
- Render the House component inside Dashboard.
- Create a
routes.js
file inside thesrc
folder. We will use this for our routing later. - Run
npm start
to make sure everything is working. You should see the names of all the components displayed.
- Run
npm i express
- Create a folder called server at the root of the project.
- Create an
index.js
file and acontroller.js
file inside of that folder. - Open
index.js
and require your packages and the controller file. - Setup a basic Express server (you will add endpoints later, just get the server ready to run).
- Open your
package.json
. Add yourmain
property (sonodemon
will work) and yourproxy
(so ouraxios
requests will work).- Your main should look like
"main": "server/index.js"
- Your proxy should look like
"proxy": "http://localhost:4000"
using whatever port your server is setup to run on (the port should not be 3000 because that is what React will be running on).
- Your main should look like
- Run
nodemon
and make sure your server runs.
- Run
npm i massive dotenv
- Create an
.env
file at the root of the project. - Open your
.gitignore
and add the.env
file to it. - Open
server/index.js
and requiremasssive
anddotenv
(make sure to invoke config ondotenv
). - Go to Heroku and create a database (you can also use a database you already have created, but just be careful not to name your table for Houser the same thing as any of the tables that already exist in your database)
- Copy the connection URI for your new or existing database and save it in your .env file (make sure you put
?ssl=true
on the end of the string). - Create a folder called
db
at the root of the project. - Set up
massive
in your server using the connection string you saved in your.env
file. - Make sure to run
nodemon
again and make sure your database is connecting. - Copy the connection string from your
.env
file intoSQLTabs
and create the houses table. - It's helpful to insert some dummy data into your database at this point to help you test as you go along.
Live example here. Filled out planning sheet here
In the first part you will set up routing, the abiltiy to view the houses, add a new house, and delete a house from the list.
Functionality of the Wizard View:
- A user should be able to add a name, address, city, state, and zipcode for a house.
- A user should be able to click the 'Cancel' button.
- This should NOT add a house to the database.
- This should redirect the user to the Dashboard.
- A user should be able to click the 'Complete' button.
- This should add a new house to the database.
- This should redirect the user to the Dashboard
Funcitonality of the Dashboard View:
- A user should be able to see all the houses that have been added to the database.
- Each house should display its name, address, city, state, and zipcode information.
- A user should be able to click the 'Add New Property' button to be taken to the Wizard view.
- A user should be able to delete any single house.
You are going to begin by setting up the routing.
- Open
App.js
and bring inHashRouter
fromreact-router-dom
. Wrap the outermostdiv
of App with theHashRouter
component. - Now open
routes.js
. Bring inSwitch
andRoute
fromreact-router-dom
. Also bring in the Dashboard component and the Wizard component. - Set up the
Switch
component as the default export of the file. - Inside the
Switch
, setup aRoute
for both of the components you brought in.- Dashboard should have '/' for its path.
- Wizard should have '/wizard' for its path.
- Open
App.js
and change what you're bringing into the component.- Remove Dashboard and Wizard from the component.
- Instead bring in routes from
routes.js
and render them instead of the other two components. - The Header component should remain, as this will show on every view.
- If you open your application in the browser you should see the header at all times, but the two views should display only when you visit the corresponding path in the URL.
- Create the 'Add New Property' button in the Dashboard view. Set it up to navigate to the Wizard view when clicked.
- Create the 'Cancel' button in the Wizard view. Set it up to navigate to the Dashboard when clicked.
Now that routing is setup, the first thing you will do is set up the form in the Wizard View.
- Set up initial state in Wizard for name, address, city, state, and zipcode.
- Create a corresponding input box in the JSX for each property in state.
- Set up the input boxes to update the correct piece of state on change.
Now you can add the ability to see all the houses.
- Setup initial state in Dashboard. You will need to store the list of houses that the server returns.
- Map over the list in your render method, returning the House component for each house (this won't display anything until we get the list from the backend).
- Write a GET endpoint in your server.
- The endpoint should respond with a list of all the houses in the database.
- Write a method in Dashboard that sends an
axios
request to the endpoint you just wrote.- Once the response comes back, update state with the list of houses.
- Invoke this method as soon as the Dashboard view loads.
- You should see the word "House" repeated as many times as you have houses in your database.
- Update your House component to display the details of each house.
- Change where you're mapping over the list in Dashboard to pass the house information down to the House component.
- In the House component write JSX to display the house information that was passed down.
- Add a 'Delete' button in the House componenet. You will make it functional later.
Next you will add the ability to add a new house.
- Write a POST endpoint in your server.
- The endpoint should pull the name, address, city, state, and zipcode off of the body.
- The endpoint should respond with the 'all good' status code once it has added the house to the database.
- Write a method in Wizard that sends an
axios
request to the endpoint you just wrote.- The
axios
request should take all the values from state and put them in the body of the request. - Once the request comes back, navigate the user to the Dashboard View.
- HINT: There is a method you can use to navigate. This will work better than a
Link
in this case.
- HINT: There is a method you can use to navigate. This will work better than a
- The
- Set up the 'Complete' button to fire the method.
Then you will add the ability to delete a house.
- Write DELETE endpoint in your server.
- The endpoint should should use a parameter to determine which house to remove from the database.
- The endpoint should respond with the 'all good' status code once it has removed the house to the database.
- Write a method in Dashboard that sends an
axios
request to the endpoint you just wrote.- The method should accept a parameter to determine which house to remove from the database.
- Once the response comes back from the server, invoke the method you wrote to get all the houses from the database.
- Pass the method from Dashboard to each House component through props.
- The method should fire when a user clicks any of the 'Delete' buttons.
- Remember to pass an argument into the method to identify which house should be deleted.
Live example here
In this part we will expand our Wizard to have three steps instead of just one.
Functionality of the Wizard:
- The Wizard should have three steps.
- A user should be able to navigate between the steps.
- The inputs on each step should be remembered. If the user selects a previous step they should see the previously entered values.
- It is acceptable to lose the values on refresh.
- In Step One:
- A user should be able to add a name, address, city, state, and zipcode for a house.
- A user should be able to click the 'Next Step' button to navigate to Step Two.
- In Step Two:
- A user should be able to add an image URL.
- A user should be able to click the 'Next Step' button to navigate to Step Three.
- A user should be able to click the 'Previous Step' button to navigate to Step One.
- In Step Three:
- A user should be able to add the monthly mortgage amount.
- This should populate a 'Recommended Rent' field. This amount should be 1.25 times the monthly mortgage amount.
- A user should be able to add the desired monthly rent.
- A user should be able to click the 'Complete' button.
- This should add a new house with all of the form values to the database.
- This should clear the inputs of the Wizard views.
- This should redirect the user to the Dashboard.
- HINT: There is a method you can use to navigate. This will work better than a
Link
in this case.
- HINT: There is a method you can use to navigate. This will work better than a
- A user should be able to add the monthly mortgage amount.
- A user should be able to click the 'Cancel' button on any step.
- This should NOT add a house to the database.
- This should clear the inputs of the Wizard.
- This should redirect the user to the Dashboard.
- HINT: There is a method you can use to navigate. This will work better than a
Link
in this case.
- HINT: There is a method you can use to navigate. This will work better than a
Functionality of the Dashboard View:
- A user should be able to see all the houses that have been added to the database.
- Each house should display its image, name, address, city, state, zipcode, montly mortgage, and desired rent information.
- A user should be able to click the 'Add New Property' button to be taken to the Wizard view.
- A user should be able to delete any single house.
Dashboard Wizard - Step One Wizard - Step Two Wizard - Step Three
In this step you will prepare your application to work with Redux.
- First, run
npm i redux
- Use
SQLTabs
to connect to your database. Alter the houses table, adding colums for the image, monthly mortgage amount, and desired rent- HINT: Save the SQL command for this to show your mentor later to earn some points.
- Create a file called
store.js
inside ofsrc
. - Now create three components, one for each step.
- Your Wizard component has most of the functionality of Step One in it right now. Move state, the methods needed for the inputs to update state, and the input boxes from Wizard to Step One.
- Leave the 'Cancel' button in Wizard.
- Build the components for steps two and three to roughly match Step One. Just change the input boxes to be different for each view.
- Move the method that saves a house to the database and the 'Complete' button from Wizard to Step Three.
- Now Wizard should be mostly empty. We are going to use it to organize our new routes.
- Bring in
Route
fromreact-router-dom
, and the three step components you just made. - Set up a route for each step. The path should look like '/wizard/step#' with the number matching each step.
- By setting up the routes for the steps inside Wizard instead of inside
routes.js
we keep our routing tree organized.
- Bring in
- Now set up the navigation buttons in each step to flip through the steps.
- Also go to the 'Add New Property' button in Dashboard and change it to navigate to '/wizard/step1' instead of '/wizard'
Now you will get the Redux store set up and talking to a component.
- Open
store.js
and bring increateStore
fromredux
. - Create an object called
initialState
. This object should store all the values entered in the wizard. - Create a function named
reducer
. This function should take in two parameters:state
(with the default value ofinitialState
), andaction
. - Set up a
switch
statement inside thereducer
based on the action type. For now just setup a default case that returns state. - Create and export a store using
createStore
andreducer
. - Go back to Step One and import
store
fromsrc/ducks/store.js
.- Create a variable called
reduxState
in theconstructor
. Set it's value to.getState
invoked. - Use values from
reduxState
to set initial state for the component.
- Create a variable called
- Subscribe to the
store
inside ofcomponentDidMount
.- Use the
.subscribe
method. - Update local state using values from Redux state.
- Use the
Then you will setup your Step One component to update Redux state.
- Open
store.js
and create and export an action type constant for Step One. - In your
reducer
function, add acase
to theswitch
statement.- The
case
should match the action type you just wrote. - This
case
should return an object that includes all the values stored onstate
. The values for img, monthly mortgage, and desired rent should remain what they were, and the values for name, address, city, state, and zipcode should be updated based on the values of the action payload.
- The
- In Step One, import the action type you created.
- Now setup the 'Next Step' button to update the
store
using the action type.- Use the
.dispatch
method. Pass in an object with atype
and apayload
. - The button should still navigate to the next step.
- Use the
Now that you have Step One connected to Redux, you will replicate the process for steps two and three.
For both components:
- Import
store
fromstore.js
. - Create a variable called
reduxState
in theconstructor
. Set it's value to.getState
invoked. - Use values from
reduxState
to set initial state for the component. - Subscribe to the
store
inside ofcomponentDidMount
.- Use the
.subscribe
method. - Update local state using values from Redux state.
- Use the
Then replicate the process of saving the values to Redux state for steps two and three.
For both components:
- Open
store.js
and create and export an action type constant. - In your
reducer
function, add acase
to theswitch
statement for each action type.- The cases should return an object that includes all the values stored on state. The values added in other steps should remain what they were, and the values for the current step should be updated based on the values of the action payload.
- Import the corresponding action type into the component.
- Now setup the buttons to update the
store
using the action type.- Use the
.dispatch
method. Pass in an object with atype
and apayload
. - The image URL should be saved to Redux when the 'Previous Step' or the 'Next Step' buttons are clicked in Step Two.
- The monthly mortgage amount and the desired rent should be saved to Redux when the 'Previous Step' button is click in Step Three.
- The buttons should still navigate correctly.
- Use the
Now you will set up you cancel button to forget all values from the Wizard.
- Open
store.js
and create and export another action type constant. - In your
reducer
function, add acase
for the action type to theswitch
statement.- The
case
should match the action type you just wrote. - The
case
should return an object. It should have all the properties of state, and the values should match what they were in theinitialState
object.
- The
- In Wizard, import the
store
. - Set up the 'Cancel' button to clear Redux State.
- Use the
.dispatch
method. Pass in an object with thetype
you imported. This time you don't need apayload
. - The button should still navigate correctly.
- Use the
Finally you will update the ability to add a new house to use all these new values
- Change the sql file you wrote for your POST endpoint to accept additional parameters and insert them into the new columns you added.
- Update the POST endpoint to pull the additional values off of the body and pass them into the massive function.
- Open Step Three and update the method that saves a new house to the database.
- The
axios
request should send all the values from all three steps on the body. - name, address, city, state, zipcode, and the image URL should be pulled from Redux, but the monthly mortgage amount and the desired rent should be pulled from local state.
- This is because Redux is only updated when the navigation buttons are clicked, so on the final step the values stored in Redux may be out of date.
- The method should invoke the action builder that clears the Redux state once the house has been added. Remember to bring the action builder in at the top of the file.
- The method should still navigate to the Dashboard once the house has been added.
- The
- Lastly update the House component to display the additional values for each House.
Congratulations! You've completed 27 competencies and built your second full-stack application!
Competencies covered by this project
"Student can use git to create, manage, and synchronize commits locally and remotely (Local and remote repository in-sync, .gitignore)"
"Student can use class based components in react and it's features (render, JSX, nested components)"
"Student can apply ES6 constructs in React for better code (import, export, destructuring)"
"Student can create Node servers using the Express package (Server running)"
"Student can create tables in a database"
"Student can connect to their database in their NodeJS servers using Massive"
Step 1
"Student can add ReactRouter to their code base (HashRouter)"
"Student can add ReactRouter to their code base (Switch, Route, component)"
"Student can add ReactRouter to their code base (Link)"
Step 2
"Student can use class based components in react and it's features (state, setState, constructors)"
"Student can use class based components in react and it's features (events)"
"Student can apply ES6 constructs in React for better code (arrow functions)"
Step 3
"Student can use class based components in react and it's features (props)"
"Student can interact with the web via axios and REST"
"Student can use componentDidMount in their code"
"Student can create a RESTful API (Status codes)"
"Student can create a RESTful API (GET endpoint)"
"Student can create SQL statements to manipulate data in their databases (Select)"
"Student can run SQL commands in their NodeJS servers using Massive"
Step 4
"Student can create a RESTful API (express.json)"
"Student can create a RESTful API (POST endpoint)"
"Student can create SQL statements to manipulate data in their databases (Insert)"
Step 5
"Student can use class based components in react and it's features (.bind)"
"Student can create a RESTful API (params)"
"Student can create a RESTful API (DELETE endpoint)"
Step 1
"Student can alter existing tables in a database"
Step 2
"Student can utilize Redux in their code to manage state (store, reducer)"
Don't worry too much about the competencies; they will be covered as you build this project.
If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.