1
votes

tl;dr: this.setState({ results: body.hits.hits }); doesn't work

I need to render the response from an ElasticSearch index in React.js v16. I do have working code for React v < 15.5:

Github

That I broke after re-writing it with ES6 class syntax:

Github

class App extends Component {
  constructor(props) {
    super(props)
      this.state = { results: [] };
    }

    handleChange(event) {
      const search_query = event.target.value;

      client.search({
            index: _index,
            type: _type,
            body: {
                query: {
                        multi_match: {
                                query: search_query,
                                fields: ['title^100', 'tags^100', 'abstract^20', 'description^10', 'chapter^5', 'title2^10', 'description2^10'],
                                fuzziness: 1,
                            },
                    },
            },
        }).then(function(body) {
            this.setState({ results: body.hits.hits });
          }.bind(this),
          function(error) {
            console.trace(error.message);
          }
        );
    }

    render() {
      return (
        <div className="container">
          <input type="text" onChange={this.handleChange} />
          <SearchResults results={this.state.results} />
        </div>
      );
    }
}

I am using an App component to set initialState results to empty array inside a constructor. Then use a handleChange function to take text inputs from the input field and send them as search queries to ES. This works, I can see the console trace for body.hits.hits, that looks something like this:

    TRACE: 2017-10-19T09:35:37Z
  -> POST http://localhost:9200/myIndex_2017_09_09/article/_search
  {
    "query": {
      "multi_match": {
        "query": "query",
        "fields": [
          "title^100",
          "tags^100",
          "abstract^20",
          "description^10",
          "chapter^5",
          "title2^10",
          "description2^10"
        ],
        "fuzziness": 1
      }
    }
  }
  <- 200
  {
    "took": 19,
    "timed_out": false,
    "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
    },
    "hits": {
      "total": 369,
      "max_score": 18.169382,
      "hits": [
        {
          "_index": "myIndex_2017_09_09",
          "_type": "article",
          "_id": "AV5mDaz7Jw6qOfpXAp1g",
          "_score": 18.169382,
          "_source": {
            "title2": "title2",
            "series": [
              "series1",
              "series2",
              "series3"
            ],
            "models": [
              "models1",
              "models2"
            ],
            "description": "description",
            "description2": "description2",
            "link": "URL",
            "title": "title",
            "chapter": "chapter",
            "tags": [
              "tags1",
              "tags2",
              "tags3"
            ],
            "image": "URL",
            "abstract": "abstract"
          }
        }
      ]
    }
  }

I then add a render function to display the input field and another stateless component SearchResults to iterate some JSX over the response. This component is passed down this.state.results, which does not seem to work:

const SearchResults = ({results}) => (
  <div className="search_results">
    <hr />

    <table>
      <thead>
        <tr>
          <th>Title</th>
        </tr>
      </thead>
      <tbody>
        {results.map((result , i) =>
          <ResultRow key={i}
                     title={result._source.title2} />
        )}
      </tbody>
    </table>
  </div>
)

const ResultRow = ({ title }) => (
  <tr>
    <td>
      {title}
    </td>
  </tr>
)

The state of SearchResults always shows up as an empty array. But when I add some dummy data to App constructor, this renders just fine:

constructor(props) {
    super(props)
      this.state = { results: [
        {
            "_index": "myIndex_2017_09_09",
            "_type": "article",
            "_id": "AV5mDXcSJw6qOfpXAp0a",
            "_score": 5.5604653,
            "_source": {
              "title2": "title 01",
            }
          },
          {
              "_index": "myIndex_2017_09_09",
              "_type": "article",
              "_id": "AV5mDXcSJw6qOfpXApsa",
              "_score": 2.1404631,
              "_source": {
                "title2": "title 02",
              }
            }
      ]}
    }

Can someone spot the error?

1

1 Answers

0
votes

i think the problem is that you haven't bound the context of handleChange

you can do that in one of two places

// in the constructor. refer below for why this is better
constructor(props) {
  // ...your stuff here

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


//or in the actual element
<input type="text" onChange={this.handleChange.bind(this)} />

you can look here for references on the style guide im following