简体   繁体   中英

Filter response from api on user search getting error :`Unhandled Rejection (TypeError): Cannot read property 'results' of undefined`

Trying to update my state with a filtered api response. I make the call and then update the results state (see below).

api call

  async searchMovies(keyword, year) {
    const response = await getMoviesFiltered(keyword, year);
    console.log(response); // filtered response
    this.setState({ results: response.results});
  }

request

export const getMoviesFiltered = async (keyword, year) => {
  try {
    const response = await axios.get(
      `https://api.themoviedb.org/3/search/movie?api_key=<apikey>&language=en-US&query=${keyword}&page=1&include_adult=false&year=${year}`
    );
    console.log(response.data.results);
    return response.data;

  } catch (error) {
    console.error(error);
  }
};

response from api:

{page: 1, results: Array(20), total_pages: 95, total_results: 1889}
page: 1
results: (20) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
total_pages: 95
total_results: 1889

I want to update my state with the response (results key, which is an array) and map over it but it tells me that Unhandled Rejection (TypeError): Cannot read property 'results' of undefined which is strange as i can see results in the response

Also, when I attempted to update the results in the state with response eg

this.setState({ results: response});

I get:

TypeError: movies.map is not a function

ie it can't map over the response i am passing down.... I have looked at other questions on stack and they say this is because the response is not an array, but mine is:S

Here is where i am mapping over the data, which is passed down from the parent.

 <MovieList movies={results || []} genres={genreOptions || []} />

component:

    return (
      <MoviesWrapper>
        {movies && movies.map((movie, index) => {
          const {
            title,
            vote_average,
            overview,
            release_date,
            poster_path,
            genre_ids,
          } = movie;
          return (
            <MovieItem
              key={index}
              title={title}
              rating={vote_average}
              overview={overview}
              release={release_date}
              poster={poster_path}
              movieGenres={matchGenres(genre_ids)}
            />
          );
        })}

can anyone see what is wrong?

Cant see your full component codes; but the problem is probably that you initialized your state null.

When you have a state defined in your code, the state initializes with its initial value.

const [Movies, setMovies] = useState(null)

Now i have defined a state called Movies and its null at the beginning.

I do my API call at useEffect block(or ComponentDidMount); and after my API response, i update the state to response with setState, or in example above; setMovies().

useEffect(() => {
   const response = await axios.get(`ENDPOINT_URL`);
   setMovies(response) 
},[]);

Now my state is the response array from my API call. Only now I can use map function, not before; because it was null and would throw an error.

Lets dive into how React handles this flow in return block. React does not wait for the API call to end to initialize the state and print what is in return block. This is important; because if i try to do something with the state at the first render; before react updates the state with the response array from api; the state will be "null".

So the order is like this.

1.Initialize state with initial value (null)

2.Render return block

3.Update state (Example: setMovies([1,2,3]) )

4.Re-render return block.

Lets say i have {console.log(Movies)} in my return block. I will see 2 logs in the console; the first one will say "null"(step 2), and the second one will say "[1,2,3]" (step 4)

Now, if i have Movies.map function inside return block; it will throw an error which will say "TypeError: Movies.map is not a function"; because Movies is null at the first.

As for your other question: "Unhandled Rejection (TypeError): Cannot read property 'results' of undefined"

This error is thrown when parent object is empty; in this case; parent object of results. Cant be sure which "results" is throwing error here because you didnt provide row number of exact error. For example; data.results is throwing this; it means "data" is empty and does not have results as child object. This is most likely result of 2 things; when we judge response object schema wrong, (Ex: response.data.movies.results instead of response.data.results) or we tried to reach data.results before we fill it "Ex: data = apiresponse.content".

If you can provide a CodeSandbox; it would be much easier to answer.

Edit after codesandbox

Error corrected: https://codesandbox.io/s/movies-app-angela-forked-ehr9u

Errors:

  • MovieList where you.map movies; the state initializes empty because of the reasons i explained above; so it throws error; You can just add {movies && movies.length > 0...} 1 step above map function to check if movies exists and isnt empty.

  • In your searchMovies function your fetcher call catches errors but only console.logs; this creates problem in searchMovies function. Because even if the response is null or errorcode, searchMovies function still continues to work, and updates the state wrongly.

  • Your search doesnt show responses in view; because after the call in searchMovies; you need to provide a direct access to results object array so view knows types. MoviesList component knows and renders array of movie objects; but after searching you set the state to the object also containing different properties; so MoviesList doesnt know how to render results. Shortly; response.data.results instead of response.data.

  • Your searchBar component, it doesnt actually search exact key press, but 1 before. Like when you try searching "dark"; it searches for "dar". When you search for "d" it searches for empty string. You have to type "dark" plus 1 space to actually search for "dark". So, api returns 422 Unprocessable entry error at first character you type because it tries to search for empty string. Its because you update the state, then call search api. But updating state is asynchronous, and state updating doesnt finish before you send state to api fetcher function. So simple solution to this would be sending search fetcher function event.target.value, instead of the state you are updating.

A small reminder: Change your API key now; as its exposed to web.

Solved the issue.

It was to do with the searchMovies api call, when i returned the api response, I have to do a check to see if there is actually a response first of all. If there is a response then I want to access the results array in that response.

Essentially do this when setting the state:

this.setState({ results: response && response.results });

instead of:

this.setState({ results: response.results });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM