Skip to content

Commit 7de5e54

Browse files
committed
chapter 3
1 parent 1ca6e3b commit 7de5e54

File tree

1 file changed

+123
-36
lines changed

1 file changed

+123
-36
lines changed

src/App.js

Lines changed: 123 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,172 @@
11
import React, { Component } from 'react';
22
import './App.css';
33

4-
const list = [
5-
{
6-
title: 'React',
7-
url: 'https://facebook.github.io/react/',
8-
author: 'Jordan Walke',
9-
num_comments: 3,
10-
points: 4,
11-
objectID: 0,
12-
},
13-
{
14-
title: 'Redux',
15-
url: 'https://github.com/reactjs/redux',
16-
author: 'Dan Abramov, Andrew Clark',
17-
num_comments: 2,
18-
points: 5,
19-
objectID: 1,
20-
},
21-
];
22-
23-
const isSearched = searchTerm => item =>
24-
item.title.toLowerCase().includes(searchTerm.toLowerCase());
4+
const DEFAULT_QUERY = 'redux';
5+
const DEFAULT_HPP = '100';
6+
7+
const PATH_BASE = 'https://hn.algolia.com/api/v1';
8+
const PATH_SEARCH = '/search';
9+
const PARAM_SEARCH = 'query=';
10+
const PARAM_PAGE = 'page=';
11+
const PARAM_HPP = 'hitsPerPage=';
2512

2613
class App extends Component {
2714
constructor(props) {
2815
super(props);
2916

3017
this.state = {
31-
list,
32-
searchTerm: '',
18+
results: null,
19+
searchKey: '',
20+
searchTerm: DEFAULT_QUERY,
21+
error: null,
3322
};
3423

24+
this.needsToSearchTopStories = this.needsToSearchTopStories.bind(this);
25+
this.setSearchTopStories = this.setSearchTopStories.bind(this);
26+
this.fetchSearchTopStories = this.fetchSearchTopStories.bind(this);
3527
this.onSearchChange = this.onSearchChange.bind(this);
28+
this.onSearchSubmit = this.onSearchSubmit.bind(this);
3629
this.onDismiss = this.onDismiss.bind(this);
3730
}
3831

32+
needsToSearchTopStories(searchTerm) {
33+
return !this.state.results[searchTerm];
34+
}
35+
36+
setSearchTopStories(result) {
37+
const { hits, page } = result;
38+
const { searchKey, results } = this.state;
39+
40+
const oldHits = results && results[searchKey]
41+
? results[searchKey].hits
42+
: [];
43+
44+
const updatedHits = [
45+
...oldHits,
46+
...hits
47+
];
48+
49+
this.setState({
50+
results: {
51+
...results,
52+
[searchKey]: { hits: updatedHits, page }
53+
}
54+
});
55+
}
56+
57+
fetchSearchTopStories(searchTerm, page = 0) {
58+
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
59+
.then(response => response.json())
60+
.then(result => this.setSearchTopStories(result))
61+
.catch(e => this.setState({ error: e }));
62+
}
63+
64+
componentDidMount() {
65+
const { searchTerm } = this.state;
66+
this.setState({ searchKey: searchTerm });
67+
this.fetchSearchTopStories(searchTerm);
68+
}
69+
3970
onSearchChange(event) {
4071
this.setState({ searchTerm: event.target.value });
4172
}
4273

74+
onSearchSubmit(event) {
75+
const { searchTerm } = this.state;
76+
this.setState({ searchKey: searchTerm });
77+
78+
if (this.needsToSearchTopStories(searchTerm)) {
79+
this.fetchSearchTopStories(searchTerm);
80+
}
81+
82+
event.preventDefault();
83+
}
84+
4385
onDismiss(id) {
86+
const { searchKey, results } = this.state;
87+
const { hits, page } = results[searchKey];
88+
4489
const isNotId = item => item.objectID !== id;
45-
const updatedList = this.state.list.filter(isNotId);
46-
this.setState({ list: updatedList });
90+
const updatedHits = hits.filter(isNotId);
91+
92+
this.setState({
93+
results: {
94+
...results,
95+
[searchKey]: { hits: updatedHits, page }
96+
}
97+
});
4798
}
4899

49100
render() {
50-
const { searchTerm, list } = this.state;
101+
const {
102+
searchTerm,
103+
results,
104+
searchKey,
105+
error,
106+
} = this.state;
107+
108+
const page = (
109+
results &&
110+
results[searchKey] &&
111+
results[searchKey].page
112+
) || 0;
113+
114+
const list = (
115+
results &&
116+
results[searchKey] &&
117+
results[searchKey].hits
118+
) || [];
119+
51120
return (
52121
<div className="page">
53122
<div className="interactions">
54123
<Search
55124
value={searchTerm}
56125
onChange={this.onSearchChange}
126+
onSubmit={this.onSearchSubmit}
57127
>
58128
Search
59129
</Search>
60130
</div>
61-
<Table
62-
list={list}
63-
pattern={searchTerm}
64-
onDismiss={this.onDismiss}
65-
/>
131+
{ error
132+
? <div className="interactions">
133+
<p>Something went wrong.</p>
134+
</div>
135+
: <Table
136+
list={list}
137+
onDismiss={this.onDismiss}
138+
/>
139+
}
140+
<div className="interactions">
141+
<Button onClick={() => this.fetchSearchTopStories(searchKey, page + 1)}>
142+
More
143+
</Button>
144+
</div>
66145
</div>
67146
);
68147
}
69148
}
70149

71-
const Search = ({ value, onChange, children }) =>
72-
<form>
73-
{children} <input
150+
const Search = ({
151+
value,
152+
onChange,
153+
onSubmit,
154+
children
155+
}) =>
156+
<form onSubmit={onSubmit}>
157+
<input
74158
type="text"
75159
value={value}
76160
onChange={onChange}
77161
/>
162+
<button type="submit">
163+
{children}
164+
</button>
78165
</form>
79166

80-
const Table = ({ list, pattern, onDismiss }) =>
167+
const Table = ({ list, onDismiss }) =>
81168
<div className="table">
82-
{list.filter(isSearched(pattern)).map(item =>
169+
{list.map(item =>
83170
<div key={item.objectID} className="table-row">
84171
<span style={{ width: '40%' }}>
85172
<a href={item.url}>{item.title}</a>

0 commit comments

Comments
 (0)