Skip to content

Instantly share code, notes, and snippets.

@krambertech
Created July 2, 2016 10:44
Show Gist options
  • Save krambertech/76afec49d7508e89e028fce14894724c to your computer and use it in GitHub Desktop.
Save krambertech/76afec49d7508e89e028fce14894724c to your computer and use it in GitHub Desktop.
ReactJS: Input fire onChange when user stopped typing (or pressed Enter key)
import React, { Component } from 'react';
import TextField from 'components/base/TextField';
const WAIT_INTERVAL = 1000;
const ENTER_KEY = 13;
export default class TextSearch extends Component {
constructor(props) {
super();
this.state = {
value: props.value
};
}
componentWillMount() {
this.timer = null;
}
handleChange(value) {
clearTimeout(this.timer);
this.setState({ value });
this.timer = setTimeout(::this.triggerChange, WAIT_INTERVAL);
}
handleKeyDown(e) {
if (e.keyCode === ENTER_KEY) {
::this.triggerChange();
}
}
triggerChange() {
const { value } = this.state;
this.props.onChange(value);
}
render() {
const { className } = this.props;
return (
<TextField
className={className}
placeholder={l('Search')}
value={this.state.value}
onChange={::this.handleChange}
onKeyDown={::this.handleKeyDown}
/>
);
}
}
@AOOlivares
Copy link

@noushad-pp
It's an ECMAScript function bind syntax as proposed here

@wootencl
Copy link

Minor note on using that :: bind syntax: Will take a slight performance hit as the function will need to be bound on every render. At least this is my understanding. To avoid this, binding should occur in the constructor.

@shenoyranjith
Copy link

Thanks! Really helpful!

@keshamtravels
Copy link

well done

@obbaeiei
Copy link

obbaeiei commented Aug 9, 2018

Thank you

But I have modified some code. It works with my project.

import React, { Component } from 'react'

const WAIT_INTERVAL = 1000
const ENTER_KEY = 13

class TextSearch extends Component {
  state = {
    value: ''
  }

  timer = null

  handleChange = e => {
    clearTimeout(this.timer)

    this.setState({ value: e.target.value })

    this.timer = setTimeout(this.triggerChange, WAIT_INTERVAL)
  }

  handleKeyDown = e => {
    if (e.keyCode === ENTER_KEY) {
      clearTimeout(this.timer)
      this.triggerChange()
    }
  }

  triggerChange = () => {
    const { value } = this.state

    this.props.onChange(value)
  }

  render() {
    const { ...rest } = this.props

    return (
      <input
        type={rest.type}
        className={rest.className}
        placeholder={rest.placeholder}
        value={this.state.value}
        onChange={this.handleChange}
        onKeyDown={this.handleKeyDown}
      />
    )
  }
}

export default TextSearch

@0t3dWCE
Copy link

0t3dWCE commented Aug 15, 2018

👍

@0t3dWCE
Copy link

0t3dWCE commented Aug 16, 2018

@obbaeiei, you should clear timeout inside handleKeyDown too. If your triggerChange will do fetch to backend (via action creator if u use redux), you will see two requests after Enter pressed.

@obbaeiei
Copy link

@0t3dWCE
I see. Thank you.

@prince1456
Copy link

Thank you its work fine for me too

@jose920405
Copy link

Any way to do this without this.setState({ value });?

Because in reality a change is made in the local state immediately and then a props is dispatched to the parent component.

@sirdarthvader
Copy link

couldn't thank you enough for this, however I was wondering if there is any better way of doing the same.

@sirdarthvader
Copy link

Thank you

But I have modified some code. It works with my project.

import React, { Component } from 'react'

const WAIT_INTERVAL = 1000
const ENTER_KEY = 13

class TextSearch extends Component {
  state = {
    value: ''
  }

  timer = null

  handleChange = e => {
    clearTimeout(this.timer)

    this.setState({ value: e.target.value })

    this.timer = setTimeout(this.triggerChange, WAIT_INTERVAL)
  }

  handleKeyDown = e => {
    if (e.keyCode === ENTER_KEY) {
      clearTimeout(this.timer)
      this.triggerChange()
    }
  }

  triggerChange = () => {
    const { value } = this.state

    this.props.onChange(value)
  }

  render() {
    const { ...rest } = this.props

    return (
      <input
        type={rest.type}
        className={rest.className}
        placeholder={rest.placeholder}
        value={this.state.value}
        onChange={this.handleChange}
        onKeyDown={this.handleKeyDown}
      />
    )
  }
}

export default TextSearch

Hey, can you explain what is type= rest.type attribute in input element.

@sirdarthvader
Copy link

Thank you
But I have modified some code. It works with my project.

import React, { Component } from 'react'

const WAIT_INTERVAL = 1000
const ENTER_KEY = 13

class TextSearch extends Component {
  state = {
    value: ''
  }

  timer = null

  handleChange = e => {
    clearTimeout(this.timer)

    this.setState({ value: e.target.value })

    this.timer = setTimeout(this.triggerChange, WAIT_INTERVAL)
  }

  handleKeyDown = e => {
    if (e.keyCode === ENTER_KEY) {
      clearTimeout(this.timer)
      this.triggerChange()
    }
  }

  triggerChange = () => {
    const { value } = this.state

    this.props.onChange(value)
  }

  render() {
    const { ...rest } = this.props

    return (
      <input
        type={rest.type}
        className={rest.className}
        placeholder={rest.placeholder}
        value={this.state.value}
        onChange={this.handleChange}
        onKeyDown={this.handleKeyDown}
      />
    )
  }
}

export default TextSearch

Hey, can you explain what is type= rest.type attribute in input element.

Never mind, got it, its the rest/spread thing you are using to pass the props passed to the component.
Thanks again.

@LucasWG
Copy link

LucasWG commented Sep 6, 2019

Thank you

But I have modified some code. It works with my project.

import React, { Component } from 'react'

const WAIT_INTERVAL = 1000
const ENTER_KEY = 13

class TextSearch extends Component {
  state = {
    value: ''
  }

  timer = null

  handleChange = e => {
    clearTimeout(this.timer)

    this.setState({ value: e.target.value })

    this.timer = setTimeout(this.triggerChange, WAIT_INTERVAL)
  }

  handleKeyDown = e => {
    if (e.keyCode === ENTER_KEY) {
      clearTimeout(this.timer)
      this.triggerChange()
    }
  }

  triggerChange = () => {
    const { value } = this.state

    this.props.onChange(value)
  }

  render() {
    const { ...rest } = this.props

    return (
      <input
        type={rest.type}
        className={rest.className}
        placeholder={rest.placeholder}
        value={this.state.value}
        onChange={this.handleChange}
        onKeyDown={this.handleKeyDown}
      />
    )
  }
}

export default TextSearch

Thanks

@OlMrGreen
Copy link

👍

@amrendra007
Copy link

amrendra007 commented Oct 4, 2019

Initialise
this.timer in constructor instead of lifecycle
constructor( ) {
super()
this.timer = null
}

@khanSoliheen
Copy link

I am trying to make an API call. But, as soon as I am writing some in the textbox. I am getting "Uncaught TypeError: this.props.onChange is not a function at TextSearch.triggerChange ". Thanks.

@sakhibgareevrr
Copy link

Would be great if someone will put the same example but with react hook (for react component) example.

@maverick42
Copy link

@vanesantillana
Copy link

vanesantillana commented Feb 16, 2021

👍

@Soleas93
Copy link

Great share ! Thank you 👍
I did the update told by @barsbek and also added a "searchChanged" boolean so there's no useless API call if the user frantically taps Enter.

    handleChange(e) {
        clearTimeout(this.timerId);
        this.setState({ search: e.target.defaultValue });
        this.searchChanged = true;
        this.timerId = setTimeout(this.updateList, SearchFolder.WAIT_INTERVAL);
    }

    handleKeyDown(e) {
        if (e.keyCode === SearchFolder.ENTER_KEY && this.searchChanged) {
            clearTimeout(this.timerId);
            this.updateList();
        }
    }

    updateList() {
        this.searchChanged = false;
        this.repository.peek(this.state.search, 0, 15, 0).then((response: FolderListItem[]) => {
            this.setState({ listItems: response });
        }).catch((error) => {
            this.setState({ item: "Erreur : " + error.message });
        });
    }

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