React: UI Flickering When State Updated

Travis S. picture Travis S. · Mar 6, 2019 · Viewed 7.3k times · Source

I have a component that displays search data returned from the Spotify API. However, every time I update the state the UI flickers:

enter image description here Input:

            <DebounceInput
                debounceTimeout={300}
                onChange={handleChange}
            />

Hook:

const [searchResults, setSearchResults] = useState(null)

API call w/ Apollo:

 const searchSpotify = async (query) => {
    const result = await props.client.query({
        query: SearchTracks,
        variables: {
            query
        }
    })
    const tracks = result.data.searchedTracks
    setSearchResults(tracks)
}

Render:

        {searchResults &&
            <div className="search-results">
                    {searchResults.map((song) => (
                            <SongInfo key={song.id} {...song} />
                    ))}
            </div>
        }

I noticed it only happens on the first load. For example, if I were to type the query again it shows without flickering. Is there a better way to implement this so the UI doesn't flicker?

Answer

varoons picture varoons · Mar 6, 2019

I think whats happening is that you are executing a search query on every key stroke which is causing the weird behavior.

Use lodash debounce to avoid doing a search on every key stroke. That should address the flickering. (Also, adding a loading state will help)

Sample debounce component

import React, {Component} from 'react'
import { debounce } from 'lodash'

class TableSearch extends Component {

  //********************************************/

  constructor(props){
    super(props)

    this.state = {
        value: props.value
    }

    this.changeSearch = debounce(this.props.changeSearch, 250)
  }

  //********************************************/

  handleChange = (e) => {
    const val = e.target.value

    this.setState({ value: val }, () => {
      this.changeSearch(val)
    })
  }

  //********************************************/

  render() {

    return (
        <input
            onChange = {this.handleChange}
            value = {this.props.value}
        />
    )
  }

  //********************************************/

}