您可以通过左右滑屏查看更多笔记内容。。
import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
// 设置好 URL 常量和默认参数,将 API 请求分解成几步
const DEFAULT_QUERY = 'redux';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
// 使用模板字符串(template strings)进行链接字符串
const url = `${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${DEFAULT_QUERY}`;
// var url = PATH_BASE + PATH_SEARCH + '?' + PARAM_SEARCH + DEFAULT_QUERY;
console.log(url);
const isSearched=searchTeam=>item=>
item.title.toLowerCase().includes(searchTeam.toLowerCase());
// 使用真实的API
class App extends Component{
constructor(props){
super(props);
this.state={
result:null,
searchTeam:DEFAULT_QUERY,
}
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.fetchSearchTopStories = this.fetchSearchTopStories.bind(this);
this.onSerachChange = this.onSerachChange.bind(this);
this.onDissmisss = this.onDissmisss.bind(this);
}
//删除函数
onDissmisss(id){
// 找到除点击外的其他id
const isNotId=item=>item.objectID!==id;
//在原数组中筛选包含其他id的一个新的数组
const updatedHits=this.state.result.hits.filter(isNotId);
// 重新设置state
this.setState({
// Object.assign() 函数:它把接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。只要把目标对象设置成一个空对象,我们就得到了一个新的对象。这种做法是拥抱不变性的,因为没有任何源对象被改变。
// result:Object.assign({},this.state.result,{hits:updatedHits})
result:{...this.state.result,hits:updatedHits}
});
}
//onChange函数,event对象的target属性中带有输入框的值
onSerachChange(event){
// 得到我输入框的内容
this.setState({searchTeam:event.target.value});
}
setSearchTopStories(result) {
this.setState({ result });
}
fetchSearchTopStories(searchTeam) {
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTeam}`)
//返回的响应需要被转化成 JSON 格式的数据结构
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
//如果在发起请求时出现错误,这个函数会进入到 catch 中而不是 then 中
.catch(e => e);
}
// 获取数据
componentDidMount() {
const { searchTeam } = this.state;
this.fetchSearchTopStories(searchTeam);
}
render(){
const { searchTeam, result } = this.state;
if (!result) { return null; }
return (
<div className="page">
<div className="interactions">
{/* 用于搜索的输入组件 */}
<Search
value={searchTeam}
onChange={this.onSerachChange}
>
Search
</Search>
</div>
<Table
list={result.hits}
pattern={searchTeam}
onDissmisss={this.onDissmisss}
/>
</div>
)
}
}
const Button=({onClick,className='',children,})=>
<button
onClick={onClick}
className={className}
type="button"
>
{children}
</button>
//Search 组件
const Search =({value,onChange,children})=>
// const {value,onChange,children}=props;
<form>
{children}<input
type="text"
value={value}
onChange={onChange}
/>
</form>
//列表样式
const Table=({list,pattern,onDissmisss})=>
<div className="table">
{list.filter(isSearched(pattern)).map(item=>
<div key={item.objectID} className="table-row">
<span style={midColumn}>
<a href={item.url}>{item.title}</a>
</span>
<span style={smallColumn}>{item.points}</span>
<span style={largeColumn}>{item.author}</span>
<span style={midColumn}>{item.num_comments}</span>
<span style={midColumn}>
<Button onClick={()=>onDissmisss(item.objectID)}
className="button-inline"
>
Dissmiss
</Button>
</span>
</div>
)}
</div>
const largeColumn = {
width: '30%',
};
const midColumn = {
width: '20%',
};
const smallColumn = {
width: '10%',
};
export default App;
const userNames = { firstname: 'Robin', lastname: 'Wieruch' };
const userAge = { age: 28 };
const user = { ...userNames, ...userAge };
console.log(user);
// output: { firstname: 'Robin', lastname: 'Wieruch', age: 28 }
组件内部状态中的 result 对象的初始值为空。当 API 的结果还没返回时,此时的 App 组 件没有返回任何元素。这已经是一个条件渲染了,因为在某个特定条件下,render() 方法提 前返回了。根据条件,App 组件渲染它的元素或者什么都不渲染。
{ result
? <Table
list={result.hits}
pattern={searchTerm}
onDismiss={this.onDismiss}
/>
: null
}
{ result &&
<Table
list={result.hits}
pattern={searchTerm}
onDismiss={this.onDismiss}
/>
}
将输入数据处触发搜索的函数改为点击按钮进行搜索;
import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
// 设置好 URL 常量和默认参数,将 API 请求分解成几步
const DEFAULT_QUERY = 'redux';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
// 使用模板字符串(template strings)进行链接字符串
const url = `${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${DEFAULT_QUERY}`;
// var url = PATH_BASE + PATH_SEARCH + '?' + PARAM_SEARCH + DEFAULT_QUERY;
console.log(url);
// 使用真实的API
class App extends Component{
constructor(props){
super(props);
this.state={
result:null,
searchTeam:DEFAULT_QUERY,
}
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.fetchSearchTopStories = this.fetchSearchTopStories.bind(this);
this.onSerachChange = this.onSerachChange.bind(this);
this.onDissmisss = this.onDissmisss.bind(this);
this.onSerachSubmit=this.onSerachSubmit.bind(this);
}
onSerachSubmit(){
const {searchTeam}=this.state;
this.fetchSearchTopStories(searchTeam);
//阻止表单提交自动重新加载页面的行为
event.preventDefault();
}
//删除函数
onDissmisss(id){
// 找到除点击外的其他id
const isNotId=item=>item.objectID!==id;
//在原数组中筛选包含其他id的一个新的数组
const updatedHits=this.state.result.hits.filter(isNotId);
// 重新设置state
this.setState({
// Object.assign() 函数:它把接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。只要把目标对象设置成一个空对象,我们就得到了一个新的对象。这种做法是拥抱不变性的,因为没有任何源对象被改变。
// result:Object.assign({},this.state.result,{hits:updatedHits})
result:{...this.state.result,hits:updatedHits}
});
}
//onChange函数,event对象的target属性中带有输入框的值
onSerachChange(event){
// 得到我输入框的内容
this.setState({searchTeam:event.target.value});
}
setSearchTopStories(result) {
this.setState({ result });
}
fetchSearchTopStories(searchTeam) {
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTeam}`)
//返回的响应需要被转化成 JSON 格式的数据结构
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
//如果在发起请求时出现错误,这个函数会进入到 catch 中而不是 then 中
.catch(e => e);
}
// 获取数据
componentDidMount() {
const { searchTeam } = this.state;
this.fetchSearchTopStories(searchTeam);
}
render(){
const { searchTeam, result } = this.state;
// 条件渲染
// if (!result) { return null; }
return (
<div className="page">
<div className="interactions">
{/* 用于搜索的输入组件 */}
<Search
value={searchTeam}
onChange={this.onSerachChange}
onSubmit={this.onSerachSubmit}
>
Search
</Search>
</div>
{result ?
<Table
list={result.hits}
onDissmisss={this.onDissmisss}
/>
:null
}
</div>
)
}
}
const Button=({onClick,className='',children,})=>
<button
onClick={onClick}
className={className}
type="button"
>
{children}
</button>
//Search 组件
const Search =({value,onChange,onSubmit,children})=>
// const {value,onChange,children}=props;
<form onSubmit={onSubmit}>
<input
type="text"
value={value}
onChange={onChange}
/>
<button type="submit">
{children}
</button>
</form>
//列表样式
const Table=({list,onDissmisss})=>
<div className="table">
//去掉了过滤
{list.map(item=>
<div key={item.objectID} className="table-row">
<span style={midColumn}>
<a href={item.url}>{item.title}</a>
</span>
<span style={smallColumn}>{item.points}</span>
<span style={largeColumn}>{item.author}</span>
<span style={midColumn}>{item.num_comments}</span>
<span style={midColumn}>
<Button onClick={()=>onDissmisss(item.objectID)}
className="button-inline"
>
Dissmiss
</Button>
</span>
</div>
)}
</div>
const largeColumn = {
width: '30%',
};
const midColumn = {
width: '20%',
};
const smallColumn = {
width: '10%',
};
export default App;
主要增加了一个抓取页数
import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
// 设置好 URL 常量和默认参数,将 API 请求分解成几步
const DEFAULT_QUERY = 'redux';
const DEFAULT_HPP = '10';////每次抓取的具体页数
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=';//每次抓取几页?
// 使用模板字符串(template strings)进行链接字符串
// output: https://hn.algolia.com/api/v1/search?query=redux&page=
// 使用真实的API
class App extends Component{
constructor(props){
super(props);
this.state={
result:null,
searchTeam:DEFAULT_QUERY,
}
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.fetchSearchTopStories = this.fetchSearchTopStories.bind(this);
this.onSerachChange = this.onSerachChange.bind(this);
this.onDissmisss = this.onDissmisss.bind(this);
this.onSerachSubmit=this.onSerachSubmit.bind(this);
}
onSerachSubmit(){
const {searchTeam}=this.state;
this.fetchSearchTopStories(searchTeam);
//阻止表单提交自动重新加载页面的行为
event.preventDefault();
}
//删除函数
onDissmisss(id){
// 找到除点击外的其他id
const isNotId=item=>item.objectID!==id;
//在原数组中筛选包含其他id的一个新的数组
console.log(this.state.result);
const updatedHits=this.state.result.hits.filter(isNotId);
// console.log(updatedHits);
// 重新设置state
this.setState({
// Object.assign() 函数:它把接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。只要把目标对象设置成一个空对象,我们就得到了一个新的对象。这种做法是拥抱不变性的,因为没有任何源对象被改变。
// result:Object.assign({},this.state.result,{hits:updatedHits})
result:{...this.state.result,hits:updatedHits}
});
}
//onChange函数,event对象的target属性中带有输入框的值
onSerachChange(event){
// 得到我输入框的内容
this.setState({searchTeam:event.target.value});
}
// 这是查找的数据为当前的状态
setSearchTopStories(result) {
const {hits,page}=result;
// 如果页数为0则,则hits 是空的,不为0就为下一页,老的 hits 已经储存在状态中等待着与新的分页合并。
const oldHits = page !== 0
? this.state.result.hits: [];
const updatedHits = [
...oldHits,
...hits
];
this.setState({
result: { hits: updatedHits, page }
})
}
fetchSearchTopStories(searchTeam,page=0) {
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTeam}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
//https://hn.algolia.com/api/v1/search?query={搜索的内容}&page=0&hitsPerPage=10
//返回的响应需要被转化成 JSON 格式的数据结构
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
//如果在发起请求时出现错误,这个函数会进入到 catch 中而不是 then 中
.catch(e => e);
}
// 获取数据
componentDidMount() {
const { searchTeam } = this.state;
this.fetchSearchTopStories(searchTeam);
}
render(){
const { searchTeam, result } = this.state;
const page=(result&& result.page)||0;
// 条件渲染
// if (!result) { return null; }
return (
<div className="page">
<div className="interactions">
{/* 用于搜索的输入组件 */}
<Search
value={searchTeam}
onChange={this.onSerachChange}
onSubmit={this.onSerachSubmit}
>
Search
</Search>
</div>
{result &&
<Table
list={result.hits}
onDissmisss={this.onDissmisss}
/>
}
<div className="interactions">
<Button onClick={() => this.fetchSearchTopStories(searchTeam, page + 1)}>
More
</Button>
</div>
</div>
)
}
}
const Button=({onClick,className='',children,})=>
<button
onClick={onClick}
className={className}
type="button"
>
{children}
</button>
//Search 组件
const Search =({value,onChange,onSubmit,children})=>
// const {value,onChange,children}=props;
<form onSubmit={onSubmit}>
<input
type="text"
value={value}
onChange={onChange}
/>
<button type="submit">
{children}
</button>
</form>
//列表样式
const largeColumn = {
width: '30%',
};
const midColumn = {
width: '20%',
};
const smallColumn = {
width: '10%',
};
const Table=({list,onDissmisss})=>
<div className="table">
{/* 去掉了过滤 */}
{list.map(item=>
<div key={item.objectID} className="table-row">
<span style={midColumn}>
<a href={item.url}>{item.title}</a>
</span>
<span style={smallColumn}>{item.points}</span>
<span style={largeColumn}>{item.author}</span>
<span style={midColumn}>{item.num_comments}</span>
<span style={midColumn}>
<Button onClick={()=>onDissmisss(item.objectID)}
className="button-inline"
>
Dissmiss
</Button>
</span>
</div>
)}
</div>
export default App;
使用results 存储每次查找的内容,
import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
// 设置好 URL 常量和默认参数,将 API 请求分解成几步
const DEFAULT_QUERY = 'redux';
const DEFAULT_HPP = '10';//每次抓取的具体页数
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=';//每次抓取几页?
// 使用真实的API
class App extends Component{
constructor(props){
super(props);
this.state={
//存储每次查找内容
results: null,
//每次单次查找的内容result
searchKey: '',
searchTeam:DEFAULT_QUERY,
}
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.fetchSearchTopStories = this.fetchSearchTopStories.bind(this);
this.onSerachChange = this.onSerachChange.bind(this);
this.onDissmisss = this.onDissmisss.bind(this);
this.onSerachSubmit=this.onSerachSubmit.bind(this);
}
needsToSearchTopStories(searchTeam) {
return !this.state.results[searchTeam];
}
// 点击Search 触发事件
onSerachSubmit(){
const {searchTeam}=this.state;
this.setState({ searchKey:searchTeam });
if (this.needsToSearchTopStories(searchTeam)) {
this.fetchSearchTopStories(searchTeam);
}
//阻止表单提交自动重新加载页面的行为
event.preventDefault();
}
//删除函数
onDissmisss(id){
const { searchKey, results } = this.state;
const { hits, page } = results[searchKey];
// 找到除点击外的其他id
const isNotId=item=>item.objectID!==id;
//在原数组中筛选包含其他id的一个新的数组
console.log(this.state.result);
const updatedHits = hits.filter(isNotId);
// console.log(updatedHits);
// 重新设置state
this.setState({
// Object.assign() 函数:它把接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。只要把目标对象设置成一个空对象,我们就得到了一个新的对象。这种做法是拥抱不变性的,因为没有任何源对象被改变。
// result:Object.assign({},this.state.result,{hits:updatedHits})
results: {
...results,
[searchKey]: { hits: updatedHits, page }
}
});
}
//onChange函数,event对象的target属性中带有输入框的值
onSerachChange(event){
// 得到我输入框的内容
this.setState({searchTeam:event.target.value});
}
// 这是查找的数据设置为当前的状态
setSearchTopStories(result) {
const {hits,page}=result;
//通过searchKey存储结果的位置
const { searchKey, results } = this.state;
// 如果页数为0则,则hits 是空的,不为0就为下一页,老的 hits 已经储存在状态中等待着与新的分页合并。
//如果在results 中没有查到,则为空,
const oldHits = results && results[searchKey]?
results[searchKey].hits: [];
const updatedHits = [
...oldHits,
...hits
];
// 将查找到的内容设置为当前状态
this.setState({
results: {
...results,
[searchKey]: { hits: updatedHits,page}
}
});
}
fetchSearchTopStories(searchTeam,page=0) {
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTeam}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
//返回的响应需要被转化成 JSON 格式的数据结构
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
//如果在发起请求时出现错误,这个函数会进入到 catch 中而不是 then 中
.catch(e => e);
}
// 获取数据
componentDidMount() {
const { searchTeam } = this.state;
this.setState({searchKey:searchTeam})
this.fetchSearchTopStories(searchTeam);
}
render(){
const {
searchTeam,
results,
searchKey
} = this.state;
const page = (
results &&
results[searchKey] &&
results[searchKey].page
) || 0;
const list = (
results &&
results[searchKey] &&
results[searchKey].hits
) || [];
// 条件渲染
// if (!result) { return null; }
return (
<div className="page">
<div className="interactions">
{/* 用于搜索的输入组件 */}
<Search
value={searchTeam}
onChange={this.onSerachChange}
onSubmit={this.onSerachSubmit}
>
Search
</Search>
</div>
<Table
list={list}
onDissmisss={this.onDissmisss}
/>
<div className="interactions">
<Button onClick={() => this.fetchSearchTopStories(searchKey, page + 1)}>
More
</Button>
</div>
</div>
)
}
}
const Button=({onClick,className='',children,})=>
<button
onClick={onClick}
className={className}
type="button"
>
{children}
</button>
//Search 组件
const Search =({value,onChange,onSubmit,children})=>
// const {value,onChange,children}=props;
<form onSubmit={onSubmit}>
<input
type="text"
value={value}
onChange={onChange}
/>
<button type="submit">
{children}
</button>
</form>
//列表样式
const largeColumn = {
width: '30%',
};
const midColumn = {
width: '20%',
};
const smallColumn = {
width: '10%',
};
const Table=({list,onDissmisss})=>
<div className="table">
{/* 去掉了过滤 */}
{list.map(item=>
<div key={item.objectID} className="table-row">
<span style={midColumn}>
<a href={item.url}>{item.title}</a>
</span>
<span style={smallColumn}>{item.points}</span>
<span style={largeColumn}>{item.author}</span>
<span style={midColumn}>{item.num_comments}</span>
<span style={midColumn}>
<Button onClick={()=>onDissmisss(item.objectID)}
className="button-inline"
>
Dissmiss
</Button>
</span>
</div>
)}
</div>
export default App;