I have an array of the names of cities. also, I have an input that I want the any input value to be searched on the array, and synchronously show the input's placeholder according to city's name that started with input value, whenever that user typing a character.
a part of cities.json:
[ "Aberdeen", "Abilene", "Akron", "Albany", "Albuquerque", "Alexandria", "Allentown", "Amarillo", "Anaheim", "Anchorage", "Ann Arbor", "Antioch", "Apple Valley", "Appleton", "Arlington", "Arvada", "Asheville", "Athens", "Atlanta", "Atlantic City", "Augusta", "Aurora", "Austin", "Bakersfield", "Baltimore", "Barnstable", "Baton Rouge", "Beaumont", . . . . . . . . . . . . . . . "Wilmington", "Winston", "Winter Haven", "Worcester", "Yakima", "Yonkers", "York", "Youngstown" ]
index.jsx:
import React from "react";
import ReactDOM from "react-dom";
import cities from "./cities.json"; //cities is an array.
class App extends React.Component{
constructor(props) {
super(props);
this.state = {
value: '',
placeholder: ''
}
}
handleChange = (e) => {
this.setState({
value: e.target.value
});
for (let i = 0; i < cities.length; i ){
if (cities[i].startsWith(`${this.state.value}`)){
this.setState({
placeholder: cities[i]
});
}
}
}
render() {
return (
<div>
<input value={this.state.value} placeholder={this.state.placeholder} onChange={this.handleChange}/>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById("root"));
But two issues:
1- Use two setState: Do second setState use result of first setState?
2- Output is not expected: typed value is not synchronous with placeholder value. placeholder is shown when the input is cleared(while the placeholder must be empty,too). also it only shows first city, with any starter letter. e.g if I type "Akro" or "Alba", it shows "Aberdeen" instead "Akron" and "Albany", as placeholder. as well as if I type "Baton", it shows "Bakersfield" instead "Baton Rouge"(note the array).
my output:
expected output:
What is your solution? :)
CodePudding user response:
the setState
function is async, so if you want to use the newly state you have to provide a callback to setState
and use the new state inside it. So, for example, in your case you have to do something like:
handleChange = (e) => {
this.setState({
value: e.target.value
}, () => {
for (let i = 0; i < cities.length; i ){
if (cities[i].startsWith(`${this.state.value}`)){
this.setState({
placeholder: cities[i]
});
}
}
});
}
CodePudding user response:
@Marco Nisi is correct but according to your question you want the saw city suggestions. @Arman placeholder is only displayed when there is no value in the input box. but you can use the datalist provided in the html. Please find the link to the working solution here.
import React from "react";
import cities from "./cities.json"; //cities is an array.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "",
placeholder: "",
suggestions: []
};
}
handleChange = (e) => {
// saving current input value to state
this.setState(
{
value: e.target.value
},
() => {
// empty the suggestions array state to remove previous suggestions
this.setState({
suggestions: []
});
for (let i = 0; i < cities.length; i ) {
if (cities[i].startsWith(`${this.state.value}`)) {
this.setState(
{
suggestions: [...this.state.suggestions, cities[i]]
});
}
}
}
);
};
render() {
return (
<div>
<label htmlFor="city">Choose your city:</label>
<input
value={this.state.value}
placeholder={this.state.placeholder}
onChange={this.handleChange}
list="suggested_cities"
name="city"
id="city"
/>
<datalist id="suggested_cities">
{this.state.suggestions.map((suggestion) => {
return <option value={suggestion} key={suggestion} />;
})}
</datalist>
</div>
);
}
}
export default App;
Link to the fiddle for working example. https://codesandbox.io/s/react-fiddle-forked-viqmt?from-embed