您可以通过左右滑屏查看更多笔记内容。。

使用真实的api

06 Sep 2019 . category: tech .
更多笔记

使用真实的API

一.获取数据+扩展操作符

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;