0

I am trying to add sorting to my movie app, I had a code that was working fine but there was too much code repetition, I would like to take a different approach and keep my code DRY. Anyways, I am confused as on which method should I set the state when I make my AJAX call and update it with a click event.

This is a module to get the data that I need for my app.

export const moviesData = {
  popular_movies: [],
  top_movies: [],
  theaters_movies: []
};

export const queries = {
  popular:
    "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=###&page=",
  top_rated:
    "https://api.themoviedb.org/3/movie/top_rated?api_key=###&page=",
  theaters:
    "https://api.themoviedb.org/3/movie/now_playing?api_key=###&page="
};

export const key = "68f7e49d39fd0c0a1dd9bd094d9a8c75";

export function getData(arr, str) {
  for (let i = 1; i < 11; i++) {
    moviesData[arr].push(str + i);
  }
}

The stateful component:

class App extends Component {
  state = { 
   movies = [],
   sortMovies: "popular_movies",
   query: queries.popular,
   sortValue: "Popularity"
  }
}

// Here I am making the http request, documentation says
// this is a good place to load data from an end point
async componentDidMount() {
    const { sortMovies, query } = this.state;
    getData(sortMovies, query);

    const data = await Promise.all(
      moviesData[sortMovies].map(async movie => await axios.get(movie))
    );
    const movies = [].concat.apply([], data.map(movie => movie.data.results));

    this.setState({ movies });
  }

In my app I have a dropdown menu where you can sort movies by popularity, rating, etc. I have a method that when I select one of the options from the dropwdown, I update some of the states properties:

handleSortValue = value => {
    let { sortMovies, query } = this.state;

    if (value === "Top Rated") {
      sortMovies = "top_movies";
      query = queries.top_rated;
    } else if (value === "Now Playing") {
      sortMovies = "theaters_movies";
      query = queries.theaters;
    } else {
      sortMovies = "popular_movies";
      query = queries.popular;
    }

    this.setState({ sortMovies, query, sortValue: value });
  };

Now, this method works and it is changing the properties in the state, but my components are not re-rendering. I still see the movies sorted by popularity since that is the original setup in the state (sortMovies), nothing is updating.

I know this is happening because I set the state of movies in the componentDidMount method, but I need data to be Initialized by default, so I don't know where else I should do this if not in this method.

I hope that I made myself clear of what I am trying to do here, if not please ask, I'm stuck here and any help is greatly appreciated. Thanks in advance.

Gilbert R.
  • 282
  • 1
  • 7
  • 19

1 Answers1

3

The best lifecycle method for fetching data is componentDidMount(). According to React docs:

Where in the component lifecycle should I make an AJAX call?

You should populate data with AJAX calls in the componentDidMount() lifecycle method. This is so you can use setState() to update your component when the data is retrieved.

Example code from the docs:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      items: []
    };
  }

  componentDidMount() {
    fetch("https://api.example.com/items")
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            isLoaded: true,
            items: result.items
          });
        },
        // Note: it's important to handle errors here
        // instead of a catch() block so that we don't swallow
        // exceptions from actual bugs in components.
        (error) => {
          this.setState({
            isLoaded: true,
            error
          });
        }
      )
  }

  render() {
    const { error, isLoaded, items } = this.state;
    if (error) {
      return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
      return <div>Loading...</div>;
    } else {
      return (
        <ul>
          {items.map(item => (
            <li key={item.name}>
              {item.name} {item.price}
            </li>
          ))}
        </ul>
      );
    }
  }
}

Bonus: setState() inside componentDidMount() is considered an anti-pattern. Only use this pattern when fetching data/measuring DOM nodes. Further reading:

Community
  • 1
  • 1
Brian Le
  • 2,646
  • 18
  • 28