skip to Main Content

The selected book depends on the URL parameter returned by the useParams hook. The selected book should also not be redefined from render to render unless the value returned by the useParams hook has changed.

I would like to know if useParams returns a persistent object from render to render.

const { books, setBooks } = useContext(BooksContext);
const { bookId = 0 } = useParams();
const selectedBook = useMemo(
  () => books.find(book => book.id === +bookId) || null,
  [books, bookId],
);

2

Answers


  1. The useParams hook in React Router does not necessarily guarantee a persistent object from render to render, as it returns an object containing the URL parameters of the current route.

    Here is an example:

    import { useParams } from 'react-router-dom';
    import { useContext, useMemo } from 'react';
    import { BooksContext } from './BooksContext';
    
    const BookDetail = () => {
      // Retrieve the books from context
      const { books } = useContext(BooksContext);
      
      const { bookId = 0 } = useParams();
      
      const selectedBook = useMemo(
        () => books.find(book => book.id === +bookId) || null,
        [books, bookId]
      );
    
      return (
        <div>
          {selectedBook ? (
            <div>
              <h1>{selectedBook.title}</h1>
              <p>{selectedBook.description}</p>
            </div>
          ) : (
            <p>No book selected.</p>
          )}
        </div>
      );
    };
    
    export default BookDetail;
    
    Login or Signup to reply.
  2. your effect depends on bookId which is a number it does not depend on the whole useParams object so if it is a different useParams object with the same value of bookId then useMemo will not be triggered. also from what I know the useParams object does not change value between renders.

    Here you may don’t need to include books in the dependecy array of useMemo there is no need to update selectedBook and filter each time books are updated and if you want to track changes for this selected book, because books may be updated and you want to track selectedBook changes so then you can add another value to your context to provide the last updated book Id so when you update a book you change its value as well to the id of this updated book and then you can do something like this:

    const { books, setBooks, lastUpdatedBookId } = useContext(BooksContext);
    const { bookId = 0 } = useParams();
    const selectedBookRef = useRef(); // just to track the selectedbook so
    // we can return it if the lastUpdatedBookId is different from bookId
    
    const selectedBook = useMemo(() => {
      if (lastUpdatedBookId === +bookId || !selectedBookRef.current) {
        let newSelectedBook = books.find((book) => book.id === +bookId) || null;
        selectedBookRef.current = newSelectedBook;
        return newSelectedBook;
      } else {
        return selectedBookRef.current;
      }
    }, [bookId, lastUpdatedBookId]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search