You've been asked to create a site for an animal shelter which allows adoption of cats and dogs. These are the only two animals allowed in the shelter. The adoption process works strictly on a first-in, first-out basis. People can adopt a cat, or a dog, or both, but they may only adopt the oldest one (by arrival time, not age) that is in the shelter.
The following instructions should guide you to through the project from start to finish. Be sure to follow them in the order!
If you’re the sort of person who likes a working example, you can see the working client and the working server for yourself.
This app will use two distinct repositories: one for the client, and one for the server.
- Create the parent directory for your app on your local machine:
mkdir AnimalShelter - Move into the directory:
cd AnimalShelter
- Clone the server template repository:
git clone https://github.com/Thinkful-Ed/backend-template server - Move into the server directory:
cd server - Install the dependencies:
npm install - Create a new repo called
animalShelter-serveron GitHub: https://github.com/new- Make sure the "Initialize this repository with a README" option is left unchecked
- Update the remote to point to your GitHub repository:
git remote set-url origin https://github.com/YOUR_GITHUB_USERNAME/animalShelter-server- You can now
git pushto theorigin masterbranch to save this repo on your GitHub
- You can now
- Move back to the project directory:
cd .. - Update
create-react-appand then use it to generate our client:npm install -g create-react-app && create-react-app client/ - Move into the client directory:
cd client - Install the Redux dependencies our app will need:
npm install --save react-redux redux redux-thunk - Initialize Git:
git init - Commit your changes:
git add -A; git commit-m "Initial commit" - Create a new repo on GitHub called
animalShelter-client: https://github.com/new - Add a new remote that points to your GitHub repository:
git remote add origin https://github.com/YOUR_GITHUB_USERNAME/animalShelter-client- Push your changes up to GitHub
Our app should be able to show us the cat or dog that has been in the shelter the longest, and also be able to remove an animal from the shelter database once it has been adopted. This will require GETing and DELETEing, respectively.
- In
server/index.jsadd a GET endpoint atapi/catwhich returns the following cat information as JSON:
{
imageURL:'https://assets3.thrillist.com/v1/image/2622128/size/tmg-slideshow_l.jpg',
imageDescription: 'Orange bengal cat with black stripes lounging on concrete.',
name: 'Fluffy',
sex: 'Female',
age: 2,
breed: 'Bengal',
story: 'Thrown on the street'
}- Run the server:
npm start - go to
http://localhost:8080/api/catand look at your cat! - In
server/index.jsadd another GET endpoint atapi/dogwhich returns a dog as JSON. You can use the following information:
{
imageURL: 'http://www.dogster.com/wp-content/uploads/2015/05/Cute%20dog%20listening%20to%20music%201_1.jpg',
imageDescription: 'A smiling golden-brown golden retreiver listening to music.',
name: 'Zeus',
sex: 'Male',
age: 3,
breed: 'Golden Retriever',
story: 'Owner Passed away'
}- Go to
http:://localhost:8080/api/dogand day hi to your dog!
- Since we want to test the modification of data, move the cat and dog data into arrays declared near the top of your
index.jsfile. - Modify your
GEThandlers so they return the item at the beginning of their respective animal arrays. - Write functions to route
DELETErequests to each of your API endpoints. Recall that the goal is to adopt the animal that has been waiting in the shelter the longest, so deletion should remove the animal at the beginning of the array.- You may want to make some more animals as dummy data.
- Each new animal you make should contain: an image of the pet; a physical description of the pet; its name, sex, age, and breed; and a story of its journey to the shelter. All of this should be in an object with the same structure as the dummy data you're already using.
- Use Postman as well to verify that your
GETandDELETEendpoints work.
Now that a basic version of our API is working, it’s time to work on the client.
- Make a new file in
client/srccalledDashboard.js- This component should take two props called
catToAdoptanddogToAdopt
- It should render two
sections: one for thecatToAdopt, and one for thedogToAdopt. - Within the
sections, there should be aheaderwith the animal’s name and photograph; beneath that, amainwith adlto display the rest of the animal’s information.- Be sure that you add an
altattribute to your images that uses the animal’s description!
- Be sure that you add an
- At the bottom of the
main, there should be a button with the textAdopt.
- This component should take two props called
- In
index.js, modify the existing call toReactDOM.renderso that it renders<Dashboard/ >instead. Pass the above animal data as the propscatToAdoptanddogToAdopt. - Run the client:
npm start - Visit http://localhost:3000. You should see both animals and the information about them.
- Create a new directory in
srccalledcomponents; in it, create a file calledPet.js.- This component should be called twice in the render method of the
Dashboard. One should receive theDashboard’scatToAdoptas its own props; the other should receivedogToAdopt. - It should use these props to render the
sectionelements thatDashboardwas previously rendering. - It should also receive a prop called
onAdoptPet, which will be a function that will be called when theAdoptbutton within thePetcomponent is clicked.- For now, this function can be a simple
console.log(); we will make it do more once we add Redux.
- For now, this function can be a simple
- Don’t forget to pass
onAdoptPetto theonClickhandler of the button within thePetcomponent so yourconsole.log()will fire!
- This component should be called twice in the render method of the
If you need a refresher on actions and reducers, take some time to read the Thinkful lesson on async actions, as well as the Redux documentation for combineReducers, which your app will need.
- Make a folder in
client/srccalledactions. In it, make a file calledcat.js- Write an async action,
fetchCat, which uses the Fetch API to make a GET request to your API’s/catendpoint and should dispatch a corresponding synchronousFETCH_CAT_SUCCESSaction. - Make sure you also write synchronous actions for initiating the request and handling errors.
- Write an async action,
- Do the same for fetching dogs as well: a
dog.jsaction file withfetchDog, and the appropriate synchronous actions. - Add an asynchronous
adoptCatandadoptDogactions (and corresponding synchronous actions) which use the Fetch API to delete an animal at the appropriate endpoint.- Remember that, once the delete request is successful, it should, dispatch
fetchCat()orfetchDog()as appropriate to make the next animal available.
- Remember that, once the delete request is successful, it should, dispatch
- Finally, you might want to make an
index.jsfile in this folder, through which you can export the actions in the other 2 files to make it easier to import those actions elsewhere.
- Make a
src/reducersfolder. The files in it should mirror the files in youractionsfolder. - You may want to use the following as a model for
catanddogslices of your state:
{
data: null,
error: null,
loading: false
}Before you write proper reducers, do the following so that you can be sure that redux is working:
- Set one of your dummy cat and dog objects as the
dataprop of initial state of its appropriate reducer. - return the
initialStatefrom each reducer - use
combineReducersinindex.jsto bring together thecatanddogreducers, then export the combined reducer.
- Create a store file at
client/src/store.jswhich uses your reducer.- Don't forget to apply the Redux Thunk middleware so your async actions work.
- In
client/src/index.js, import thestore. You should be able to usestore.getState()to see your cat and dog information. - Now you can write proper reducers. Set the
dataprop of each reducer back tonull, then handle your request, success, and error actions.
- With your actions hooked up, create a
client/.env.development.localand aclient/src/config.jsfile.- In
.env.development.local, create the variableREACT_APP_API_BASE_URL=http://localhost:8080/api- adding
.development.localto the filename means the variables in this file won’t be read when we build the app. You can read more about whencreate-react-appwill read a particular.envfile. - The
REACT_APP_prefix allowscreate-react-appto send a variable to the app when we runnpm run start
- adding
- In
config.jsexportconst REACT_APP_API_BASE_URLand set it equal toprocess.env.REACT_APP_API_BASE_URL.
- In
- Import that constant from
config.jsand use it in your actions.
- In
client/src/index.js, wrap your component in the React ReduxProvidercomponent so it has access to the store. - Remove the props which you currently pass to the
Dashboardcomponent. - Use React Redux to connect your
Dashboardto the store, mapping thecatanddogpart of the state to thecatToAdoptanddogToAdoptprops. - In the
componentDidMountmethod of yourDashboardcomponent, dispatch thefetchCatandfetchDogactions to fetch the their respective animals. - Try reloading your app. You should see both animals.
- Once you know the actions run in
componentDidMount, change theonAdoptPetprop of eachPetso that, instead of console-logging, it dispatchesadoptCatoradoptDogas appropriate. - Now, you should see one of each animal when the page loads, and get a new animal by clicking one of the adopt buttons. Test that both adopt buttons work!
If you have your app deployed, you should look for places to DRY out your code. Do you see any repeated patterns that could be replaced with simpler code? After thinking about that, give the following extension tasks a try:
- Right now, our app shows users both a cat and a dog by default. Add functionality so that the user can do the following:
- choose to see a cat or a dog or both
- choose to see the animal that has been in the shelter the longest, regardless of whether it is a cat or a dog
- Update your backend so that cat or dog is stored in either a Mongo or a Postgres database. Hint: You can make use of the as-yet-ignored
dbConnectvariables inserver/index.js