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.

7
  • 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? Commented Aug 3, 2017 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 Commented Aug 3, 2017 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. Commented Aug 3, 2017 at 14:45
  • It comes in as an array with a single string inside Commented Aug 3, 2017 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; } Commented Aug 3, 2017 at 14:48

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.