Reputation: 529
I'm trying to create a search component that checks the JSON of the API(currently just a mock). The Component loads fine, but when I start to type into the search field, then the App crashes with the error "Uncaught (in promise) TypeError: Cannot read property of 'map' of undefined".
I've made sure that the fields are all the same. As mentioned the component loads correctly, but fails once the Suggestion is supposed to load. So, the problem is obviously in this file, but can't understand why it can't map the results.
Here is my Search Component:
// Imports
class Search extends Component {
state = {
query: '',
results: []
}
getInfo = () => {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(({data}) => {
this.setState({
results: data.data
})
})
}
handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getInfo()
}
} else if (!this.state.query) {
}
})
}
render() {
return (
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Suggestions results={this.state.results} />
</form>
);
}
}
export default Search;
Then the Suggestions component used in the Search component:
import React from 'react';
const Suggestions = (props) => {
const options = props.results.map(r => (
<li key={r.id}>
{r.name}
</li>
))
return <ul>{options}</ul>
}
export default Suggestions;
The ideal outcome would be for the Search to filter out based on the input.
Upvotes: 3
Views: 95
Reputation: 652
The biggest problem you have here is that you've already destructured data from the axios response, so you don't have to use data.data
, use only data
in your setState()
call. data.data
is always undefined
and that's why it fails with Cannot read property of 'map' of undefined
.
Second, I would not use a ref
for such a simple case. You can fetch the input element and its value from the event itself (you get it as the first argument of the handleInputChange()
method).
With this in mind, I would make the Search
component into looking like that:
class Search extends React.Component {
state = {
query: "",
results: []
};
getInfo = () => {
axios.get("https://jsonplaceholder.typicode.com/users").then(({ data }) => {
this.setState({
results: data
});
});
};
handleInputChange = event => {
this.setState(
{
query: event.target.value
},
() => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getInfo();
}
} else if (!this.state.query) {
}
}
);
};
render() {
return (
<form>
<input placeholder="Search for..." onChange={this.handleInputChange} />
<Suggestions results={this.state.results} />
</form>
);
}
}
export default Search;
Note how I'm not using the ref
anymore and how the data is being accessed.
Upvotes: 1
Reputation: 20504
Sometimes the most obvious answer is the correct one. If props.results
is undefined, and its value comes from state.results
then state.results
must be undefined.
I tested your code, and as long as state.results
was an array, everything worked hunky dory. I would check to make sure that data.data
is really the right place to get your results from (maybe just data
will do the trick?).
If the result legitimately is undefined at times, then check for that outcome and return an empty array:
getInfo = () => {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(({data}) => {
this.setState({
results: data.data || [] // <-- now never undefined.
})
});
}
Upvotes: 2