Last active
August 20, 2018 02:12
-
-
Save pca2/a497f2c40e4499370328a2b118e998c5 to your computer and use it in GitHub Desktop.
Chapter3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="root"></div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
1 changed file with 37 additions and 14 deletions. -- Add client cache
1 changed file with 10 additions and 3 deletions. -- Add error handling
2 changed files with 3 additions and 3 deletions -- switch from fetch to axios
1 changed file with 8 additions and 2 deletions. -- add _ismounted check to help with aborting requests for components that unmounted
1 changed file with 2 additions and 1 deletion. -- My own edit. added link to the HN page on the comments count
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
1 changed file with 21 additions and 4 deletions.: Add pagination