Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
48 views
in Technique[技术] by (71.8m points)

javascript - why can i not update my state with my api response? data is an array of objects

I have two api requests that return JSON objects. They return an array of objects.

One API request that I make is fine and allows me to update the state with the response, but the other one (below) doesn't and I don't understand why.

API request to fetch genres list:

 async getGenreList() {
    const genresResults = await getGenres();
    return genresResults;
  }

The request:

export const getGenres = async () => {
  try {
    const response = await axios.get(
      "https://api.themoviedb.org/3/genre/movie/list?api_key=<APIKEY>&language=en-US"
    );
    const { genres } = response.data;
    return genres;
  } catch (error) {
    console.error(error);
  }
};

The response is an array of 19 genre objects but this is just an example:

[ 
{id: 28, name: "Action"},
{id: 12, name: "Adventure"}
]

I then want to update the state like this and pass the response to genreOptions. But it tells me Error: Objects are not valid as a React child (found: object with keys {id, name}). If you meant to render a collection of children, use an array instead.

   componentDidMount() {
    this.getGenreList().then((response) => {
      console.log(response)
      this.setState({ genreOptions: response});
    });
  }

The below works when i update the state and map over it but I don't want to do that, i want to pass the whole response down so i can map over the data in my component as I need it there to do some data matching.

   this.setState({ genreOptions: response.map((genreOption) => {
        return genreOption.name
   })});

This is the state:

  this.state = {
      results: [],
      movieDetails: null,
      genreOptions: [],
      
    };

I want to pass the genreOptions here to genres then map over it in the MovieResults component.

 <MovieResults>
          {totalCount > 0 && <TotalCounter>{totalCount} results</TotalCounter>}
          <MovieList movies={results || []} genres={genreOptions || []} />

        </MovieResults>

Why can't I? Any ideas? I have done it for another similar request :S

UPDATE TO SHOW MOVIELIST COMPONENT

export default class MovieList extends React.Component {
  render() {

    const { movies, genres } = this.props;
  
    const testFunction = (movieGenreIds) => {
    const matchMovieGenresAndGenreIds = genres.map((genreId) => {
      const matchedGenres = movieGenreIds.find((movieGenre) => {
        return movieGenre.id === genreId
      })
      return matchedGenres // this returns the matching objects
    })
      const result = matchMovieGenresAndGenreIds.filter(Boolean).map((el)=> {
      return el.name
    })
    return result
  }
  
    return (
        <MoviesWrapper>
          {movies.map((movie) => {
            const {
              title,
              vote_average,
              overview,
              release_date,
              poster_path,
              genre_ids
            } = movie;
            return (
              <MovieItem
                title={title}
                rating={vote_average}
                overview={overview}
                release={release_date}
                poster={poster_path}
                movieGenres={testFunction(genre_ids)}

              />
            );
          })}
        </MoviesWrapper>

    );
  }
}

**** MOVIE ITEM COMPONENT***

export default class MovieItem extends React.Component {
  render() {
    const { title, overview, rating, release, poster, movieGenres } = this.props;
    return (
      // The MovieItemWrapper must be linked to the movie details popup
      <MovieItemWrapper>
        <LeftCont>
          <img
            className="movie-img"
            src={`https://image.tmdb.org/t/p/w500${poster}`}
          />
        </LeftCont>
        <RightCont>
         <div className="movie-title-container">
             <h2 className="movie-title">{title}</h2>
             <Rating>{rating}</Rating>
         </div>
             <div>{movieGenres}</div>
             <p>{overview}</p>
             <p>{release}</p>
        </RightCont>
      </MovieItemWrapper>
    );
  }
}
question from:https://stackoverflow.com/questions/65643766/why-can-i-not-update-my-state-with-my-api-response-data-is-an-array-of-objects

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Please follow this steps to fix your code. I'll try yo explain what's happening along the way:

  1. In your main component. Set the state to the value that you really want to pass to your child component. Remember that response will be an array of objects.
componentDidMount() {
    this.getGenreList().then((response) => {
        this.setState({genreOptions: response});
    });
}
  1. In your MovieList component. Please check your testFunction to respect data types. The following code will return you an array of strings containing the names of the genres that are included in the movies genres array.
const testFunction = (movieGenreIds) => {
    return genres
        .filter((genre) => {
            return movieGenreIds.includes(genre.id);
        })
        .map((genre) => genre.name);
};
  1. In your MovieItem component. (This is were the real problem was)

Instead of:

<div>{movieGenres}</div>

You may want to do something like this:

<div>{movieGenres.join(' ')}</div>

This converts your array into a string that can be rendered. Your error was due to the fact that you were passing there an array of objects that React couldn't render.

If you have any doubt, please let me know.

NOTE: I suggest you to use a type checker to avoid this kind of problems. And to be consistent with your variables naming conventions.


Update based on new information from chat:

  1. In your ExpandableFilters component, you must fix the following piece of code to get the genre name (string). As explained in chat, you can't have objects as a result for a JSX expression ({}), but only primitives that can be coerced to strings, JSX elements or an array of JSX elements.
<GenreFilterCont marginTop>
    {filtersShown && (
        <ExpandableFiltersUl>
            {this.props.movieGenres.map((genre, index) => {
                return (
                    <ExpandableFiltersLi key={index}>
                        <Checkbox />
                        {genre.name}
                    </ExpandableFiltersLi>
                );
            })}
        </ExpandableFiltersUl>
    )}
</GenreFilterCont>

Please also note that I've added a key property. You should do it whenever you have a list of elements to render. For more about this I will refer you to the React Docs.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...