Home > Web Front-end > JS Tutorial > Taking React-TanStack-Query to the Next Level in Next.js

Taking React-TanStack-Query to the Next Level in Next.js

Linda Hamilton
Release: 2025-01-22 20:40:10
Original
506 people have browsed it

Taking React-TanStack-Query to the Next Level in Next.js

Recall our previous discussion about replacing the traditional fetch useState useEffect approach with React-TanStack-Query? If you've been using the fundamentals—setting up QueryProvider, writing basic queries, and handling mutations—you've likely already experienced the advantages. However, we've only just begun to explore its capabilities.

Let's delve deeper into advanced techniques that will significantly enhance your data fetching, transforming your application's performance from "good" to "exceptionally fast."

Building a Stronger Foundation

Our previous guide demonstrated a basic movie listing using React-TanStack-Query:

<code class="language-javascript">const { data: movies, error, isLoading } = useQuery(['movies'], fetchMovies);</code>
Copy after login
Copy after login

This is a great starting point, but what if we aim for Netflix-level responsiveness? Let's elevate our techniques.

Advanced Techniques for Superior Performance

1. Intelligent Prefetching (Achieving Netflix-like Speed)

Our initial movie list required users to wait after clicking. We can drastically improve this:

<code class="language-javascript">// components/MovieList.jsx
import { useQueryClient } from '@tanstack/react-query';

export default function MovieList() {
  const queryClient = useQueryClient();

  // Leveraging our existing fetchMovies function
  const prefetchMovie = async (movieId) => {
    await queryClient.prefetchQuery({
      queryKey: ['movie', movieId],
      queryFn: () => fetchMovieDetails(movieId),
      // Maintain freshness for 5 minutes
      staleTime: 5 * 60 * 1000,
    });
  };

  return (
    <div className="grid grid-cols-4 gap-4">
      {movies.map(movie => (
        <div key={movie.id} onMouseEnter={() => prefetchMovie(movie.id)}
          className="movie-card"
        >
          {movie.title}
        </div>
      ))}
    </div>
  );
}</code>
Copy after login
Copy after login

Now, as users hover over a movie, the details are pre-loaded—instantaneous access upon clicking! ✨

2. Enhancing Mutations (Remember those?)

Our initial article covered basic mutations. Let's optimize them with optimistic updates:

<code class="language-javascript">// hooks/useUpdateMovie.js
export function useUpdateMovie() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: updateMovie,
    // The key improvement
    onMutate: async (newMovie) => {
      // Halt ongoing refetches
      await queryClient.cancelQueries(['movie', newMovie.id]);

      // Store current state (for rollback if needed)
      const previousMovie = queryClient.getQueryData(['movie', newMovie.id]);

      // Immediate (optimistic) update
      queryClient.setQueryData(['movie', newMovie.id], newMovie);

      return { previousMovie };
    },
    // Error handling
    onError: (err, newMovie, context) => {
      queryClient.setQueryData(
        ['movie', newMovie.id],
        context.previousMovie
      );
    },
  });
}</code>
Copy after login
Copy after login

3. Parallel Data Loading (Eliminate Unnecessary Waiting)

Sequential loading is a thing of the past:

<code class="language-javascript">// pages/movie/[id].js
export default function MoviePage({ movieId }) {
  const results = useQueries({
    queries: [
      {
        queryKey: ['movie', movieId],
        queryFn: () => fetchMovie(movieId),
      },
      {
        queryKey: ['cast', movieId],
        queryFn: () => fetchCast(movieId),
      },
      {
        queryKey: ['reviews', movieId],
        queryFn: () => fetchReviews(movieId),
      },
    ],
  });

  if (results.some(result => result.isLoading)) {
    return <LoadingSpinner />;
  }

  const [movie, cast, reviews] = results.map(r => r.data);

  return <MovieDetails cast={cast} movie={movie} reviews={reviews} />;
}</code>
Copy after login
Copy after login

4. Implementing Infinite Scrolling (The Efficient Way)

Let's upgrade our paginated example to seamless infinite scrolling:

<code class="language-javascript">// components/InfiniteMovieList.jsx
import { useInfiniteQuery } from '@tanstack/react-query';
import { useInView } from 'react-intersection-observer';

export default function InfiniteMovieList() {
  const { ref, inView } = useInView();

  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery({
    queryKey: ['movies'],
    queryFn: fetchMoviePage,
    getNextPageParam: (lastPage) => lastPage.nextCursor,
  });

  useEffect(() => {
    if (inView && hasNextPage) {
      fetchNextPage();
    }
  }, [inView, hasNextPage]);

  return (
    <div>
      {data.pages.map((page) => (
        page.movies.map((movie) => (
          <MovieCard key={movie.id} movie={movie} />
        ))
      ))}

      <div ref={ref}>
        {isFetchingNextPage ? <LoadingSpinner /> : null}
      </div>
    </div>
  );
}</code>
Copy after login

5. Leveraging Next.js 14 Server Components

This is a feature unavailable in our first article: Next.js 14 server component integration:

<code class="language-javascript">const { data: movies, error, isLoading } = useQuery(['movies'], fetchMovies);</code>
Copy after login
Copy after login

Pro Tips (Lessons Learned)

  1. Consistent Query Keys: Refine our movie query keys:
<code class="language-javascript">// components/MovieList.jsx
import { useQueryClient } from '@tanstack/react-query';

export default function MovieList() {
  const queryClient = useQueryClient();

  // Leveraging our existing fetchMovies function
  const prefetchMovie = async (movieId) => {
    await queryClient.prefetchQuery({
      queryKey: ['movie', movieId],
      queryFn: () => fetchMovieDetails(movieId),
      // Maintain freshness for 5 minutes
      staleTime: 5 * 60 * 1000,
    });
  };

  return (
    <div className="grid grid-cols-4 gap-4">
      {movies.map(movie => (
        <div key={movie.id} onMouseEnter={() => prefetchMovie(movie.id)}
          className="movie-card"
        >
          {movie.title}
        </div>
      ))}
    </div>
  );
}</code>
Copy after login
Copy after login
  1. Intelligent Refetching: Improve our basic refetch on window focus:
<code class="language-javascript">// hooks/useUpdateMovie.js
export function useUpdateMovie() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: updateMovie,
    // The key improvement
    onMutate: async (newMovie) => {
      // Halt ongoing refetches
      await queryClient.cancelQueries(['movie', newMovie.id]);

      // Store current state (for rollback if needed)
      const previousMovie = queryClient.getQueryData(['movie', newMovie.id]);

      // Immediate (optimistic) update
      queryClient.setQueryData(['movie', newMovie.id], newMovie);

      return { previousMovie };
    },
    // Error handling
    onError: (err, newMovie, context) => {
      queryClient.setQueryData(
        ['movie', newMovie.id],
        context.previousMovie
      );
    },
  });
}</code>
Copy after login
Copy after login
  1. Robust Error Handling: Enhance our basic error handling:
<code class="language-javascript">// pages/movie/[id].js
export default function MoviePage({ movieId }) {
  const results = useQueries({
    queries: [
      {
        queryKey: ['movie', movieId],
        queryFn: () => fetchMovie(movieId),
      },
      {
        queryKey: ['cast', movieId],
        queryFn: () => fetchCast(movieId),
      },
      {
        queryKey: ['reviews', movieId],
        queryFn: () => fetchReviews(movieId),
      },
    ],
  });

  if (results.some(result => result.isLoading)) {
    return <LoadingSpinner />;
  }

  const [movie, cast, reviews] = results.map(r => r.data);

  return <MovieDetails cast={cast} movie={movie} reviews={reviews} />;
}</code>
Copy after login
Copy after login

Choosing the Right Technique

  • Basic Queries (from our first article): Ideal for simple data fetching.
  • Prefetching: Best when user actions are predictable.
  • Parallel Queries: Use when multiple independent data sets are needed.
  • Infinite Queries: Suitable for lengthy, scrollable lists.
  • Optimistic Updates: Employ for that instantaneous user experience.

Conclusion

We've significantly advanced from our initial setup! These enhancements are crucial for creating a truly exceptional user experience.

Remember, you don't need to implement everything at once. Start with the basics, then progressively incorporate these optimizations as needed.

The next time someone comments on your application's speed, you'll know exactly why it's so impressive. ?

Happy coding! React-TanStack-Query offers endless possibilities. What should we explore next?

The above is the detailed content of Taking React-TanStack-Query to the Next Level in Next.js. For more information, please follow other related articles on the PHP Chinese website!

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template