Skip to content

Instantly share code, notes, and snippets.

@pca2
Last active August 20, 2018 02:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pca2/a497f2c40e4499370328a2b118e998c5 to your computer and use it in GitHub Desktop.
Save pca2/a497f2c40e4499370328a2b118e998c5 to your computer and use it in GitHub Desktop.
Chapter3
<div id="root"></div>
const DEFAULT_QUERY = 'redux';
const DEFAULT_HPP = '100';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
const PARAM_PAGE = 'page='
const PARAM_HPP = 'hitsPerPage=';
const url = `${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${DEFAULT_QUERY}&${PARAM_PAGE}`;
const LINK_BASE = 'https://news.ycombinator.com/item?id=';
const isSearched = searchTerm => item => item.title.toLowerCase().includes(searchTerm.toLowerCase())
class App extends React.Component {
_isMounted = false;
constructor(props) {
super(props);
this.state = {
results: null,
searchKey: '',
searchTerm: DEFAULT_QUERY,
error: null,
};
this.needstoSearchTopStories = this.needstoSearchTopStories.bind(this);
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.onDismiss = this.onDismiss.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
this.onSearchSubmit = this.onSearchSubmit.bind(this);
this.fetchSearchTopStories = this.fetchSearchTopStories.bind(this);
}
needstoSearchTopStories(searchTerm) {
return !this.state.results[searchTerm];
}
setSearchTopStories(result) {
const { hits, page} = result;
const { searchKey, results } = this.state;
const oldHits = results && results[searchKey]
? results[searchKey].hits
: [];
const updatedHits = [
...oldHits,
...hits
];
this.setState({ results: { ...results, [searchKey]: {hits: updatedHits, page}}});
}
onSearchSubmit(event) {
const { searchTerm } = this.state;
this.setState({ searchKey: searchTerm});
if (this.needstoSearchTopStories(searchTerm)) {
this.fetchSearchTopStories(searchTerm);
}
event.preventDefault();
}
fetchSearchTopStories(searchTerm, page = 0) {
axios(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
.then(result => this._isMounted && this.setSearchTopStories(result.data))
.catch(error => this._isMounted && this.setState({error}));
}
componentDidMount() {
this._isMounted = true;
const { searchTerm } = this.state;
this.setState({ searchKey: searchTerm});
this.fetchSearchTopStories(searchTerm);
}
componentWillUnmount() {
this._isMounted = false;
}
onSearchChange(event) {
this.setState({ searchTerm: event.target.value });
}
onDismiss(id){
const { searchKey, results } = this.state;
const { hits, page } = results[searchKey];
const updatedHits = hits.filter(item => item.objectID !== id);
this.setState({
results: {
...results,
[searchKey]: { hits: updatedHits, page }
}
});
}
render() {
const { searchTerm, results, searchKey, error } = this.state;
const page = (
results &&
results[searchKey] &&
results[searchKey].page
) || 0;
const list = (
results &&
results[searchKey] &&
results[searchKey].hits
) || [];
return (
<div className="page">
<div className="interactions">
<Search
value={searchTerm}
onChange={this.onSearchChange}
onSubmit={this.onSearchSubmit}
>
Search
</Search>
</div>
{ error
? <div className="interactions">
<h2>Error encountered with API</h2>
</div>
:
<Table
list={list}
onDismiss={this.onDismiss}
/>
}
<div className="interactions">
<Button onClick={() => this.fetchSearchTopStories(searchTerm, page + 1) }>
More
</Button>
</div>
</div>
);
}
}
const Search = ({ value, onChange, children, onSubmit }) => (
<form onSubmit={onSubmit}>
<input
type="text"
value={value}
onChange={onChange}
/>
<button type="submit">
{children}
</button>
</form>
)
const Table = ({ list, onDismiss }) => (
<div className="table">
{list.map(item =>
<div key={item.objectID} className="table-row">
<span style={{width: '40%'}} >
<a href={item.url}>{item.title}</a>
</span>
<span style={{width: '30%'}} >{item.author}</span>
<span style={{width: '10%'}} ><a href={`${LINK_BASE}${item.objectID}`} > {item.num_comments}</a> </span>
<span style={{width: '10%'}} >{item.points}</span>
<span style={{width: '10%'}} >
<Button
onClick={() => onDismiss(item.objectID)}
className="button-inline"
>
Dismiss
</Button>
</span>
</div>
)}
</div>
);
const Button = ({onClick,className,children= ''}) => (
<button onClick={onClick} className={className} type="button" >
{children}
</button>
)
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
body {
color: #222;
background: #f4f4f4;
font: 400 14px CoreSans, Arial, sans-serif;
}
a {
color: #222;
}
a:hover {
text-decoration: underline;
}
ul, li {
list-style: none;
padding: 0;
margin: 0;
}
input {
padding: 10px;
border-radius: 5px;
outline: none;
margin-right: 10px;
border: 1px solid #dddddd;
}
button {
padding: 10px;
border-radius: 5px;
border: 1px solid #dddddd;
background: transparent;
color: #808080;
cursor: pointer;
}
button:hover {
color: #222;
}
*:focus {
outline: none;
}
.page {
margin: 20px;
}
.interactions {
text-align: center;
}
.table {
margin: 20px 0;
}
.table-header {
display: flex;
line-height: 24px;
font-size: 16px;
padding: 0 10px;
justify-content: space-between;
}
.table-empty {
margin: 200px;
text-align: center;
font-size: 16px;
}
.table-row {
display: flex;
line-height: 24px;
white-space: nowrap;
margin: 10px 0;
padding: 10px;
background: #ffffff;
border: 1px solid #e3e3e3;
}
.table-header > span {
overflow: hidden;
text-overflow: ellipsis;
padding: 0 5px;
}
.table-row > span {
overflow: hidden;
text-overflow: ellipsis;
padding: 0 5px;
}
.button-inline {
border-width: 0;
background: transparent;
color: inherit;
text-align: inherit;
-webkit-font-smoothing: inherit;
padding: 0;
font-size: inherit;
cursor: pointer;
}
.button-active {
border-radius: 0;
border-bottom: 1px solid #38BB6C;
}
@pca2
Copy link
Author

pca2 commented Jul 23, 2018

1 changed file with 21 additions and 4 deletions.: Add pagination

@pca2
Copy link
Author

pca2 commented Jul 24, 2018

1 changed file with 37 additions and 14 deletions. -- Add client cache

@pca2
Copy link
Author

pca2 commented Jul 26, 2018

1 changed file with 10 additions and 3 deletions. -- Add error handling

@pca2
Copy link
Author

pca2 commented Jul 27, 2018

2 changed files with 3 additions and 3 deletions -- switch from fetch to axios

@pca2
Copy link
Author

pca2 commented Jul 27, 2018

1 changed file with 8 additions and 2 deletions. -- add _ismounted check to help with aborting requests for components that unmounted

@pca2
Copy link
Author

pca2 commented Jul 27, 2018

1 changed file with 2 additions and 1 deletion. -- My own edit. added link to the HN page on the comments count

@pca2
Copy link
Author

pca2 commented Aug 20, 2018

1 changed file with 7 additions and 2 deletions. -- fix typo. page count was not getting updated correctly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment