Akusas
Akusas

Reputation: 529

Property is undefined when using 'map'

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

Answers (2)

Andrei Glingeanu
Andrei Glingeanu

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

Daniel Gimenez
Daniel Gimenez

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

Related Questions