0

Im running React and Rails 5.2 with webpacker.

I have an ElasticSearch Search bar at the top of the page, and I am having trouble sending the proper request to the back end, and allowing the rails back end to process the search request.

We are no where near ready to have this as a SPA right now, but I cant seem to get the params to be populated.

import React, {Component} from 'react';
import ReactDOM from 'react-dom';

import {asyncContainer, Typeahead} from 'react-bootstrap-typeahead';

const AsyncTypeahead = asyncContainer(Typeahead);

class SearchBar extends Component {


    constructor(props) {
        super(props)
        this.state = {
            options: ['Please Type Your Query'],
            searchPath: '/error_code/search',
            selected: [""],
        }

        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }

    searchErrorCodes(term) {
        fetch(`/error_code/auto?query=${term}`)
            .then(resp => resp.json())
            .then(json => this.setState({options: json}))
    }


    handleChange(error_code_name) {
        let newValue = error_code_name

        this.setState({
            selected: newValue
        });

        this.setState({
            searchPath: `/error_code/search?error_code=${this.state.selected}`,
        });


        console.log(`This is the new searchPath is  ${this.state.searchPath}`);
    }

    handleSubmit(e) {
        alert(`submitted: ${this.state.selected}`);
        // event.preventDefault();
    }


    render() {

        return (
            <div>
                <form ref="form"
                      action={this.state.searchPath}
                      acceptCharset="UTF-8"
                      method="get"
                      onSubmit={e => this.handleSubmit(e)}
                >
                    <AsyncTypeahead
                        onSearch={term => this.searchErrorCodes(term)}
                        options={this.state.options}
                        className="search"
                        onClick={e => this.handleSubmit(e)}
                        selected={this.state.selected}
                        onChange={e => this.handleChange(e)}
                    />
                    <button
                        action={this.state.searchPath}
                        acceptCharset="UTF-8"
                        method="get"
                        type="submit"
                        className="btn btn-sm btn-search">
                        <i className="fa fa-search"></i>
                    </button>
                </form>
            </div>
        )
    }
}

ReactDOM.render(<SearchBar/>, document.querySelector('.search-bar'));

Everything is rendering correctly, but the input is not being sent properly to the controller.

R.J. Robinson
  • 2,180
  • 3
  • 21
  • 33
  • When you trigger `this.setState({ searchPath: ``/error_code/search?error_code=${this.state.selected}``, });`, it sends the old value of `this.state.selected` and not the one you just set above? – Denialos Aug 03 '17 at 14:32
  • As far as I can tell it gets updated but the form button or something does not pass on that update when you click on submit. And it sends no data to the backend – R.J. Robinson Aug 03 '17 at 14:34
  • I will add to my answer after your answer this question. How are you getting `error_code_name` from `onChange={e => this.handleChange(e)}`? The inline es6 arrow function is extraneous in this case, and will also cause performance issues and break shallow comparisons. – Kyle Richardson Aug 03 '17 at 14:45
  • It comes in as an array with a single string inside – R.J. Robinson Aug 03 '17 at 14:46
  • The way you have it right now you're setting `error_code_name` to the event not the value of the input. Try `handleChange( e ) { let newValue = e.target.value; }` – Kyle Richardson Aug 03 '17 at 14:48
  • I tried it that way. From the typeahead component, for some reason an even is not being passed. I have assigned the selected values properly. It's the get request to the backend that is not sending correctly. – R.J. Robinson Aug 03 '17 at 14:49
  • I'll update the code when I can, but I'm afk, and there is a fire drill going on. – R.J. Robinson Aug 03 '17 at 14:50

1 Answers1

1

setState is asynchronous in nature and multiple setState calls in short order can results in a batch update. Meaning the last update wins. Your second setState call is overriding the first one.

Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.

Considering you aren't doing any calculation from the previous state or props... You should change your setState call to the following:

this.setState({
    selected: newValue,
    searchPath: `/error_code/search?error_code=${newValue}`
});

You can also use a function as the updater for setState(updater, [callback]) if you need the pervious state or props to calculate the new state.

this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});

Both prevState and props received by the updater function are guaranteed to be up-to-date. The output of the updater is shallowly merged with prevState.

As an aside: Please have a look at Why JSX props should not use arrow functions. It's rather detrimental to use them inline.

Kyle Richardson
  • 5,567
  • 3
  • 17
  • 40