3

I've made a Twitch API widget which you can see here: https://twitch-react-drhectapus.herokuapp.com/

At the moment, any time you search for something, there will be a list of suggestions. I'd like to make it so that when you click on one of the datalist options it will search for that user, rather than having to click on the 'Search' button. Basically the same search function as google has.

How do I go about implementing this?

Code:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUser, fetchSuggestions } from '../actions/index';

class SearchBar extends Component {
  constructor(props) {
    super(props);
    this.state = {
      term: ''
    };
    this.onInputChange = this.onInputChange.bind(this);
    this.onFormSubmit = this.onFormSubmit.bind(this);
  }

  onInputChange(event) {
    this.setState({
      term: event.target.value
    });
  setTimeout( this.props.fetchSuggestions(event.target.value), 300);
  }

  renderSuggestions(sug, i) {
    return (
      <option key={i} value={sug.display_name} />
    )
  }

  onFormSubmit(event) {
    event.preventDefault();
    this.props.fetchUser(this.state.term);
    this.setState({
      term: ''
    });
  }

  render() {
    const { error, suggestions } = this.props;
    return (

        <form
          className='input-group'
          onSubmit={this.onFormSubmit}>
          <input
            className='form-control'
            placeholder='Search for a Twitch user'
            value={this.state.term}
            onChange={this.onInputChange}
            list='suggestions' />
          <span className='input-group-btn'>
            <button className='btn btn-primary'>
              Search
            </button>
          </span>
          <datalist id='suggestions'>
            {suggestions.map(this.renderSuggestions)}
          </datalist>
        </form>


        // {/* {error && <div className='alert alert-danger'>{error}</div>} */}


    )
  }
}

function mapStateToProps({ error, suggestions }) {
  return { error, suggestions };
}

export default connect(mapStateToProps, { fetchUser, fetchSuggestions })(SearchBar);
doctopus
  • 5,349
  • 8
  • 53
  • 105
  • What happens when you click the Search button? You don't seem to have any functionality for that. – Chris May 10 '17 at 17:28
  • The button is wrapped by a form element, which has an onSubmit function – doctopus May 10 '17 at 17:36
  • Indeed. So I assume you want the form submitted when you click the button - basically you want to trigger the `onFormSubmit` function? Bear in mind that it is only a button and not a "submit" button so it won't actually trigger anything. – Chris May 10 '17 at 17:40
  • The button isn't the issue here. I would like it to trigger the `onFormSubmit` function when i click on one of the datalist options – doctopus May 10 '17 at 17:51
  • I deleted my answer because, sadly, there is no reasonable way of accomplishing what you are looking for. Apparently what you are looking for is [nearly impossible](http://stackoverflow.com/questions/21587189). Have a look [here](http://stackoverflow.com/questions/30022728) as well. The only thing you can detect is if the `input` element was changed, but there doesn't seem to be possible to detect if the input was typed, or selected from the dropdown with click, or selected from the dropdown with the arrow keys on the keyboard. :-/ – Chris May 11 '17 at 07:24
  • Hey @Chris, i figured this was the case. I did research myself and came to the same conclusion. Appreciate the time you took to help out regardless :) – doctopus May 11 '17 at 18:54

1 Answers1

0

My solution is to test event.nativeEvent.inputType against the list of possible values to determine how the user entered the value. For my use case, I only need to differentiate between "typed" and "anything else" so my solution may not be completely exhaustive.

Input type is undefined for an option click.

const userDidntTypeIt = (inputType) => {
  return [
    undefined,
    'insertFromYank',
    'insertFromDrop',
    'insertFromPasteAsQuotation',
    'insertTranspose',
    'insertCompositionText',
    'insertLink',
  ].includes(inputType);
};

const handleOnChange = (e, onOptionClick, onValueType) => {
    const { inputType } = e.nativeEvent;
    const { value: targetValue } = e.target;
    if (userDidntTypeIt(inputType)) onOptionClick?.(targetValue);
    else onValueType?.(targetValue);
  };

And then your data list should look something like this:

const onOptionClick = (option) => console.log('clicked', option);
const onValueType = (value) => console.log('typed', value);

const onChange = (e) => handleOnChange(e, onOptionClick, onValueType);

return (
  <input
    type="text"
    list="data"
    value={value}
    onChange={onChange}
  />
  <datalist>
    {options.map((item) => <option key={item}>{item}</option>)}
  </datalist>
);
Raphael
  • 441
  • 1
  • 6
  • 12