Async data retrieval via input box is one step behind
I am making a simple Wikipedia results viewer depending on the user's input.
The results list should update anytime the input box changes.
I am using $.getJSON()
to make the call to the Wikipedia API. However, my results list doesn't update whenever the input box is changed. In fact, it seems like it's one step behind, so there must be something I don't comprehend with regards to updating the state, specifically from within an async call.
Having said that, the response does change as expected, as I can see in the console, so something is happening in the rendering of the results list that I don't comprehend.
From the React Debug Tools and the console, I can see that my array is updating as expected, as such:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Wiki Searcher</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
</head>
<body>
<div id="container"></div>
<script src="script.js"></script>
</body>
</html>
Javascript
var SearchBox = React.createClass({
changeText: function () {
this.props.changeInputText(ReactDOM.findDOMNode(this.refs.searchBox).value);
},
render: function () {
return (
<input type="text" onChange={this.changeText} ref="searchBox"/>
)
}
});
var SearchItemList = React.createClass({
render: function () {
console.log('data:', this.props.results);
console.log('inputtext', this.props.inputText);
var listItems = this.props.results
.filter(function (e, i) {
return e.title.indexOf(this.props.inputText) !== -1;
}.bind(this))
.map(function (e, i) {
return (
<li key={i}>
<a target="_blank" rel="nofollow noreferrer" target="_blank" rel="nofollow noreferrer" href="#"><h3>{e.title}</h3></a>
<p>{e.snippet}</p>
</li>
)
});
return (
<ul>
{listItems}
</ul>
)
}
});
//TODO: extract list item into its own component?
var Main = React.createClass({
getInitialState: function () {
return {
inputText: 'javascript',
results: [],
}
},
getWikiData: function () {
var prependage = 'https://en.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch='
var appendage = "&callback=?";
var url = prependage + this.state.inputText + appendage;
$.getJSON(url, function (data) {
this.setState(
{results: data.query.search}
);
}.bind(this));
},
componentDidMount: function () {
this.getWikiData();
},
changeInputText: function (searchTerm) {
this.setState({
inputText: searchTerm,
});
this.getWikiData();
},
render: function () {
return (
<div className="container">
<div className="col-sm-8 col-sm-offset-2">
<h1>Wiki Searcher</h1>
<SearchBox inputText={this.state.inputText} changeInputText={this.changeInputText}/>
<SearchItemList results={this.state.results} inputText={this.state.inputText}/>
</div>
</div>
)
}
});
ReactDOM.render(<Main/>, document.getElementById('container'));
Answer
The primary error is in your assumption that calling setState
, in your changeInputText
function, will have changed inputText
before you call getWikiData
.
setState
is an async function and so the new value for inputText
may not be set when your getWikiData
fires.
So to fix, call getWikiData
in the callback which will fire when the new state is updated.
changeInputText: function (searchTerm) {
this.setState({
inputText: searchTerm,
}, () => {
this.getWikiData();
});
},
Related Questions
- → How to update data attribute on Ajax complete
- → October CMS - Radio Button Ajax Click Twice in a Row Causes Content to disappear
- → Octobercms Component Unique id (Twig & Javascript)
- → Passing a JS var from AJAX response to Twig
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → DropzoneJS & Laravel - Output form validation errors
- → Import statement and Babel
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM