如何在事件 onChange 上设置超时

Posted

技术标签:

【中文标题】如何在事件 onChange 上设置超时【英文标题】:How to set timeout on event onChange 【发布时间】:2019-01-22 03:50:24 【问题描述】:

我有一个显示图像的画廊,我有一个搜索文本框 我正在尝试在输入事件上使用超时来防止对我输入的每个字母进行 api 调用: 我尝试使用 doSearch 函数 onChange 处理事件:但现在我无法在文本框上写任何内容,这会导致很多错误 附加到此会话的应用程序和图库组件

提前致谢

class App extends React.Component 
  static propTypes = 
  ;

  constructor() 
    super();
    this.timeout =  0;
    this.state = 
      tag: 'art'
    ;
  


  doSearch(event)
    var searchText = event.target.value; // this is the search text
    if(this.timeout) clearTimeout(this.timeout);
    this.timeout = setTimeout(function()this.setState(tag: event.target.value) , 500);
  

  render() 
    return (
      <div className="app-root">
        <div className="app-header">
          <h2>Gallery</h2>
          <input className="input" onChange=event => this.doSearch(event) value=this.state.tag/>
        </div>
        <Gallery tag=this.state.tag/>
      </div>
    );
  


export default App;

这是图库类:

import React from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import Image from '../Image';
import './Gallery.scss';

class Gallery extends React.Component 
  static propTypes = 
    tag: PropTypes.string
  ;

  constructor(props) 
    super(props);
    this.state = 
      images: [],
      galleryWidth: this.getGalleryWidth()

    ;
  

  getGalleryWidth()
    try 
      return document.body.clientWidth;
     catch (e) 
      return 1000;
    
  
  getImages(tag) 
    const getImagesUrl = `services/rest/?method=flickr.photos.search&api_key=522c1f9009ca3609bcbaf08545f067ad&tags=$tag&tag_mode=any&per_page=100&format=json&safe_search=1&nojsoncallback=1`;
    const baseUrl = 'https://api.flickr.com/';
    axios(
      url: getImagesUrl,
      baseURL: baseUrl,
      method: 'GET'
    )
      .then(res => res.data)
      .then(res => 
        if (
          res &&
          res.photos &&
          res.photos.photo &&
          res.photos.photo.length > 0
        ) 
          this.setState(images: res.photos.photo);
        
      );
  

  componentDidMount() 
    this.getImages(this.props.tag);
    this.setState(
      galleryWidth: document.body.clientWidth
    );
  

  componentWillReceiveProps(props) 
    this.getImages(props.tag);
  

  render() 
    return (
      <div className="gallery-root">
        this.state.images.map((dto , i) => 
          return <Image key='image-' + dto.id+ i.toString() dto=dto galleryWidth=this.state.galleryWidth/>;
        )
      </div>
    );
  

【问题讨论】:

只需用箭头函数 (setTimeout(() =&gt; this.setState(...))) 替换您传递给 setTimeout 的函数以保留词法范围。 【参考方案1】:

首先为什么需要使用 setTimeout 来设置用户输入的值。我看不出在 doSearch 函数中使用 setTimeout 有什么用处。

你的 doSearch 函数不能工作的原因是你没有绑定它。

您可以通过以下方式直接在doSearch函数中使用setState为标签设置值。

ES5方式

constructor(props)
    super(props);
    this.doSearch = this.doSearch.bind(this);


doSearch(event)
    this.setState(
       tag: event.target.value
    );

ES6 方式

doSearch = (event) => 
    this.setState(
       tag: event.target.value
    );

在 doSearch 函数的 setTimeout 内执行 setState 将不起作用,因为 输入标签已分配值。

ES5方式

constructor(props)
    super(props);
    this.doSearch = this.doSearch.bind(this);


doSearch(event)
     if(this.timeout) clearTimeout(this.timeout);
     this.timeout = setTimeout(function()
       this.setState(
          tag: event.target.value
       ) 
     .bind(this),500);

以 ES6 方式设置超时

doSearch = (event) => 
     if(this.timeout) clearTimeout(this.timeout);
     this.timeout = setTimeout(() => 
       this.setState(
          tag: event.target.value
       ) 
     ,500);

图库组件:

检查当前 props 的更改与 componentWillRecieveProps 中的先前更改,以避免额外的渲染。

试试下面更新的代码

import React from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import Image from '../Image';
import './Gallery.scss';

class Gallery extends React.Component 
  static propTypes = 
    tag: PropTypes.string
  ;

  constructor(props) 
    super(props);
    this.state = 
      images: [],
      galleryWidth: this.getGalleryWidth()

    ;
  

  getGalleryWidth()
    try 
      return document.body.clientWidth;
     catch (e) 
      return 1000;
    
  
  getImages(tag) 
    const getImagesUrl = `services/rest/?method=flickr.photos.search&api_key=522c1f9009ca3609bcbaf08545f067ad&tags=$tag&tag_mode=any&per_page=100&format=json&safe_search=1&nojsoncallback=1`;
    const baseUrl = 'https://api.flickr.com/';
    axios(
      url: getImagesUrl,
      baseURL: baseUrl,
      method: 'GET'
    )
      .then(res => res.data)
      .then(res => 
        if (
          res &&
          res.photos &&
          res.photos.photo &&
          res.photos.photo.length > 0
        ) 
          this.setState(images: res.photos.photo);
        
      );
  

  componentDidMount() 
    this.getImages(this.props.tag);
    this.setState(
      galleryWidth: document.body.clientWidth
    );
  

  componentWillReceiveProps(nextProps) 
     if(nextProps.tag != this.props.tag)
        this.getImages(props.tag);
     
  

  shouldComponentUpdate(nextProps, nextState) 
      if(this.props.tag == nextProps.tag)
          return false;
      else
          return true;
      
  

  render() 
    return (
      <div className="gallery-root">
        this.state.images.map((dto , i) => 
          return <Image key='image-' + dto.id+ i.toString() dto=dto galleryWidth=this.state.galleryWidth/>;
        )
      </div>
    );
  

我将标签初始值保留为空,因为您没有对价值艺术做任何事情。

请尝试以下代码

class App extends React.Component 
  static propTypes = 
  ;

  constructor() 
    super();
    this.timeout =  0;
    this.state = 
      tag: '',
      callGallery: false
    ;
  


  doSearch = (event) => 
    this.setState(tag: event.target.value, callGallery: false);
  

  handleSearch = () => 
     this.setState(
        callGallery: true
     );
  

  render() 
    return (
      <div className="app-root">
        <div className="app-header">
          <h2>Gallery</h2>
          <input className="input" onChange=event => this.doSearch(event) value=this.state.tag/>
         <input type="button" value="Search" onClick=this.handleSearch />
        </div>
        this.state.callGallery && <Gallery tag=this.state.tag/>
      </div>
    );
  


export default App;

【讨论】:

您好,首先谢谢您,我不会使用 timout 来防止大量多余的 api 调用。我不知道为什么,但是您的解决方案不适用于这种情况,我仍然无法在输入框中写任何内容:( 是的,您不能输入,因为您在 settimeout 中执行 setState,所以当您输入时它不起作用。所以设置没有超时的状态它会起作用。顺便说一句,多余的 api 调用是什么意思? 我输入的每个字母都会刷新页面,这会使 oage 运行非常缓慢,我希望在 timout pass 之后我会进行搜索 能不能也分享一下你的Gallery组件代码,不然帮到你有点难 我已经在主留言中上传了图库组件,谢谢【参考方案2】:

这是因为您没有将this 绑定到您的方法。

将以下内容添加到您的构造函数中:

this.doSearch = this.doSearch.bind(this);

另外,onChange 不需要粗箭头符号。做吧:

onChange=this.doSearch

【讨论】:

【参考方案3】:

onChange handler 可以,但是需要绑定setTimeout 来渲染上下文。目前是指窗口上下文。代码如下

   doSearch(event)
        var searchText = event.target.value; // this is the search text
        if(this.timeout) clearTimeout(this.timeout);
        this.timeout = setTimeout(function()
                         this.setState(
                             tag: event.target.value
                         ) 
                       .bind(this),500);
      

【讨论】:

以上是关于如何在事件 onChange 上设置超时的主要内容,如果未能解决你的问题,请参考以下文章

如何手动触发 onchange 事件? [复制]

如何手动触发 onchange 事件? [复制]

如何将 onchange 事件附加到 CheckBoxList?缺少输入属性?

如何在UnChecked时触发单选按钮的OnChange事件

onchange事件如何将日期值传递给javascript

如何在文本框中使用 onchange() 事件获取旧值