R.J. Robinson
R.J. Robinson

Reputation: 2240

Passing an attribute from a react component to a rails back end

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.

Upvotes: 0

Views: 1041

Answers (1)

Kyle Richardson
Kyle Richardson

Reputation: 5645

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.

Upvotes: 1

Related Questions