Skip to content

Commit cd783ea

Browse files
committed
finished development. App fully functional.
1 parent 169c30c commit cd783ea

File tree

8 files changed

+157
-22
lines changed

8 files changed

+157
-22
lines changed

README.md

+10-6
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,24 @@ The entire project is centered in the src directory with the configuration files
44

55
This repository hosts the 'Adopt Pets' project taught by btholt in the Frontend Masters' Complete Intro to React course.
66

7-
#### Timeline:
7+
### Timeline:
88

9-
##### Commit 1:
9+
#### Commit 1:
1010

11-
As of 02.09.20, I have set up the formatter and bundler and written raw React in the App.js and Pet.js files. Will be moving on to JSX next.
11+
As of 02.09.20, I have set up the formatter and bundler and written raw React in the <b>App.js</b> and <b>Pet.js</b> files. Will be moving on to JSX next.
1212

13-
##### Commit 2:
13+
#### Commit 2:
1414

1515
This is the second commit of 02.09.20. I have worked my way down with hooks using "useState" and also creating custom hooks like "useDropdown" in <b>useDropdown.js</b> to increase reusability of code. The other thing I did was include effects by calling "useEffects" that enabled me to call an external API and query it for data related to pet adoption. The locations for the search query are limited to <b>Seattle, WA</b> and <b>San Francisco, CA</b>.
1616

17-
##### Commit 3:
17+
#### Commit 3:
1818

1919
This is the third commit of 02.09.20. This commit does not have as much work going into it as the precious one but it is significant because the core idea of the 'Adopt Me!' project is now working. The application can now successfully query the external API for information and also display the results. I have used the <b>async-await</b> utility here to receive the data from the API. <b>Pet.js</b> was updated to make the results presentable and <b>Results.js</b> was created to display the results. The cross-env tool was used in the project so that it may work flawlessly on any OS. Also, the <b>dev:mock</b> script was written inside <b>package.json</b> to make the project render offline.
2020

21-
##### Commit 4:
21+
#### Commit 4:
2222

2323
This is the first commit of 03.09.20. At the end of this commit, I have a completely functional 'Adopt Me!' website for animal adoption. In this commit, I have created a details page which displays the details of the animal the user clicks on. The code for this is on <b>Details.js</b>. The details page also has a photo carousel that shows the photos of the animal. These pages used the fundamental idea of Classes in React and we had to reconfigure <b>.eslintrc.json</b> and create <b>.babelrc</b> to use the child class with readability. We also used the concept of <b>Routing</b> in React using the <b>Reach Router</b> and reconfigured a couple of js files. Will work on error handling and the completion of the project next.
24+
25+
#### Commit 5:
26+
27+
This the second commit of 03.09.20. The development of the project has now been successfully completed and the adoption app is fully functional. Among new additions we have <b>ErrorBoundary.js</b> for error handling in case the external API behaves erratically. I also have added global contexts in terms of the theme whcih is applicable throughout the application. The code for this is in <b>ThemeContext.js</b>. As a final step towards the completion of the app, I have added a functionality to the Submit button that results in the displaying of a modal which in turn, redirects to the adoption page. The code for the modal is in <b>Modal.js</b>. Will be working on the publishing of this app in the future via gh-pages and also explore publishing using React.

src/App.js

+14-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import React from "react";
1+
import React, { useState } from "react";
22
import { render } from "react-dom";
33
import { Router, Link } from "@reach/router";
44
import SearchParams from "./SearchParams";
55
import Details from "./Details";
6+
import ThemeContext from "./ThemeContext";
67

78
const App = () => {
89
// return React.createElement("div", {}, [
@@ -24,17 +25,20 @@ const App = () => {
2425
// }),
2526
// ]);
2627
// };
28+
const theme = useState("darkblue");
2729
return (
2830
<React.StrictMode>
29-
<div>
30-
<header>
31-
<Link to="/">Adopt Me!</Link>
32-
</header>
33-
<Router>
34-
<SearchParams path="/" />
35-
<Details path="/details/:id" />
36-
</Router>
37-
</div>
31+
<ThemeContext.Provider value={theme}>
32+
<div>
33+
<header>
34+
<Link to="/">Adopt Me!</Link>
35+
</header>
36+
<Router>
37+
<SearchParams path="/" />
38+
<Details path="/details/:id" />
39+
</Router>
40+
</div>
41+
</ThemeContext.Provider>
3842
</React.StrictMode>
3943
);
4044
};

src/Details.js

+47-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import React from "react";
22
import pet from "@frontendmasters/pet";
33
import Carousel from "./Carousel";
4+
import ErrorBoundary from "./ErrorBoundary";
5+
import ThemeContext from "./ThemeContext";
6+
import { navigate } from "@reach/router";
7+
import Modal from "./Modal";
48

59
class Details extends React.Component {
6-
state = { loading: true };
10+
state = { loading: true, showModal: false };
711

812
componentDidMount() {
913
pet
@@ -17,28 +21,67 @@ class Details extends React.Component {
1721
media: animal.photos,
1822
breed: animal.breeds.primary,
1923
loading: false,
24+
url: animal.url,
2025
});
2126
})
2227
.catch((error) => this.setState({ error: error }));
2328
}
29+
30+
toggleModal = () => this.setState({ showModal: !this.state.showModal });
31+
adopt = () => navigate(this.state.url);
32+
2433
render() {
2534
if (this.state.loading) {
2635
return <h1>loading...</h1>;
2736
}
28-
const { animal, breed, location, description, media, name } = this.state;
37+
const {
38+
animal,
39+
breed,
40+
location,
41+
description,
42+
media,
43+
name,
44+
showModal,
45+
} = this.state;
2946

3047
return (
3148
<div className="details">
3249
<Carousel media={media} />
3350
<div>
3451
<h1>{name}</h1>
3552
<h2>{`${animal} - ${breed} - ${location}`}</h2>
36-
<button>Adopt {name}</button>
53+
<ThemeContext.Consumer>
54+
{([theme]) => (
55+
<button
56+
style={{ backgroundColor: theme }}
57+
onClick={this.toggleModal}
58+
>
59+
Adopt {name}
60+
</button>
61+
)}
62+
</ThemeContext.Consumer>
3763
<p>{description}</p>
64+
{showModal ? (
65+
<Modal>
66+
<div>
67+
<h1>Would you like to adopt {name}?</h1>
68+
<div className="buttons">
69+
<button onClick={this.adopt}>Yes</button>
70+
<button onClick={this.toggleModal}>No</button>
71+
</div>
72+
</div>
73+
</Modal>
74+
) : null}
3875
</div>
3976
</div>
4077
);
4178
}
4279
}
4380

44-
export default Details;
81+
export default function DetailsErrorBoundary(props) {
82+
return (
83+
<ErrorBoundary>
84+
<Details {...props} />
85+
</ErrorBoundary>
86+
);
87+
}

src/ErrorBoundary.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// mostly code from reactjs.org/docs/error-boundaries.html
2+
3+
import React from "react";
4+
import { Link, Redirect } from "@reach/router";
5+
6+
class ErrorBoundary extends React.Component {
7+
constructor(props) {
8+
super(props);
9+
this.state = { hasError: false, redirect: false };
10+
}
11+
12+
static getDerivedStateFromError() {
13+
return { hasError: true };
14+
}
15+
16+
componentDidCatch(error, info) {
17+
console.error("Error Boundary caught an error", error, info);
18+
}
19+
20+
componentDidUpdate() {
21+
if (this.state.hasError) {
22+
setTimeout(() => this.state({ redirect: true }), 5000);
23+
}
24+
}
25+
26+
render() {
27+
if (this.state.redirect) {
28+
return <Redirect to="/" />;
29+
}
30+
if (this.state.hasError) {
31+
return (
32+
<h1>
33+
There was an error with this listing. <Link to="/">Click Here</Link>{" "}
34+
to go back to the home page or wait 5 seconds...
35+
</h1>
36+
);
37+
}
38+
39+
return this.props.children;
40+
}
41+
}
42+
43+
export default ErrorBoundary;

src/Modal.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React, { useEffect, useRef } from "react";
2+
import { createPortal } from "react-dom";
3+
4+
const modalRoot = document.getElementById("modal");
5+
6+
const Modal = ({ children }) => {
7+
const elRef = useRef(null);
8+
if (!elRef.current) {
9+
elRef.current = document.createElement("div");
10+
}
11+
12+
useEffect(() => {
13+
modalRoot.appendChild(elRef.current);
14+
return () => modalRoot.removeChild(elRef.current);
15+
}, []);
16+
17+
return createPortal(<div>{children}</div>, elRef.current);
18+
};
19+
20+
export default Modal;

src/SearchParams.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import React, { useState, useEffect } from "react";
1+
import React, { useState, useEffect, useContext } from "react";
22
import pet, { ANIMALS } from "@frontendmasters/pet";
33
import Results from "./Results";
44
import useDropdown from "./useDropdown";
5+
import ThemeContext from "./ThemeContext";
56

67
const SearchParams = () => {
78
const [location, setLocation] = useState("Seattle, WA");
89
const [breeds, setBreeds] = useState([]);
910
const [animal, AnimalDropdown] = useDropdown("Animal", "dog", ANIMALS);
1011
const [breed, BreedDropdown, setBreed] = useDropdown("Breed", "", breeds);
1112
const [pets, setPets] = useState([]);
13+
const [theme, setTheme] = useContext(ThemeContext);
1214

1315
async function requestPets() {
1416
const { animals } = await pet.animals({
@@ -49,7 +51,20 @@ const SearchParams = () => {
4951
</label>
5052
<AnimalDropdown />
5153
<BreedDropdown />
52-
<button>Submit</button>
54+
<label htmlFor="location">
55+
Theme
56+
<select
57+
value="theme"
58+
onChange={(e) => setTheme(e.target.value)}
59+
onBlur={(e) => setTheme(e.target.value)}
60+
>
61+
<option value="peru">Peru</option>
62+
<option value="darkblue">Dark Blue</option>
63+
<option value="chartreuse">Chartreuse</option>
64+
<option value="mediumorchid">Medium Orchid</option>
65+
</select>
66+
</label>
67+
<button style={{ backgroundColor: theme }}>Submit</button>
5368
</form>
5469
<Results pets={pets} />
5570
</div>

src/ThemeContext.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createContext } from "react";
2+
3+
const ThemeContext = createContext(["green", () => {}]);
4+
5+
export default ThemeContext;

src/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<link rel="stylesheet" href="./style.css" />
88
</head>
99
<body>
10+
<div id="modal"></div>
1011
<div id="root">not rendered</div>
1112
<script src="./App.js"></script>
1213
</body>

0 commit comments

Comments
 (0)