useParams钩子是否会在不同渲染之间返回一个持久对象?

huangapple go评论79阅读模式
英文:

Does the useParams hook return a persistent object from render to render?

问题

所选的书籍取决于useParams钩子返回的URL参数。所选的书籍还不应该在每次渲染时重新定义,除非useParams钩子返回的值发生了变化。

我想知道useParams是否在每次渲染时返回一个持久对象。

const { books, setBooks } = useContext(BooksContext);
const { bookId = 0 } = useParams();

const selectedBook = useMemo(
  () => books.find(book => book.id === +bookId) || null,
  [books, bookId],
);
英文:

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],
);

答案1

得分: 1

你的效果依赖于 bookId,它是一个数字,不依赖于整个 useParams 对象,所以如果有一个不同的 useParams 对象,但 bookId 的值相同,那么 useMemo 将不会被触发。而且据我所知,useParams 对象在渲染之间不会改变值。

在这里,你可能不需要在 useMemo 的依赖数组中包含 books,不需要在每次更新 books 时都更新 selectedBook 并进行筛选。如果你想要跟踪所选书籍的更改,因为 books 可能会更新,你希望跟踪 selectedBook 的更改,那么你可以在上下文中添加另一个值,用于提供最后更新的书籍 ID。这样,当你更新一本书时,也将其值更改为此更新后的书籍的 ID,然后你可以像这样做:

const { books, setBooks, lastUpdatedBookId } = useContext(BooksContext);
const { bookId = 0 } = useParams();
const selectedBookRef = useRef(); // 用于跟踪所选书籍,以便在 lastUpdatedBookId 与 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]);
英文:

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]);

答案2

得分: 1

短期内来看,不会返回一个持久的对象引用,而是在每次渲染周期中返回一个新的对象引用。如果你愿意,这很容易测试:

useParams钩子是否会在不同渲染之间返回一个持久对象?

const params = useParams();
const { bookId = 0 } = useParams();

useEffect(() => {
  console.log("Effect 1, params updated", { params });
}, [params]);

useEffect(() => {
  console.log("Effect 2, bookId updated", { bookId });
}, [bookId]);

忽略了由于 React.StrictMode 组件的初始双挂载,每次点击 "rerender" 按钮都会触发第一个效果,而不会触发第二个效果。

所有 路由路径参数都是字符串类型,而字符串总是等于它们自己,即使 它们是不同的引用。


const a = "foo";
const b = a;
const c = "foo";

console.log(a === b); // true
console.log(a === c); // true
console.log(b === c); // true

对于你的代码意味着,无论 bookId 是否是一个不同的字符串引用,它在用作 useMemo 钩子的依赖值时都没有关系。只要 bookId保持不变,它不会触发所选书籍的重新计算。由于路由路径参数始终是字符串,我建议将你要比较的值转换为字符串,以便代码使用类型安全的比较并且包括数字样式字符串和字母数字 id 值,而不是将 bookId 值强制转换为数字类型。

const { books, setBooks } = useContext(BooksContext);
const { bookId = 0 } = useParams();

const selectedBook = useMemo(
  () => books.find(book => String(book.id) === bookId) || null,
  [books, bookId],
);
英文:

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

In short terms, no, it returns a new object reference each render cycle. This is trivial to test if you like:

useParams钩子是否会在不同渲染之间返回一个持久对象?

const params = useParams();
const { bookId = 0 } = useParams();

useEffect(() => {
  console.log("Effect 1, params updated", { params });
}, [params]);

useEffect(() => {
  console.log("Effect 2, bookId updated", { bookId });
}, [bookId]);

Ignoring the initial double-mounting because of the React.StrictMode component, each click of the "rerender" button triggers the first effect each time, and never the second effect.

All route path params are string types, and strings are always equal to themselves, even when they are different references.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const a = &quot;foo&quot;;
const b = a;
const c = &quot;foo&quot;;

console.log(a === b); // true
console.log(a === c); // true
console.log(b === c); // true

<!-- end snippet -->

What this means for your code is that bookId regardless if it's a different string reference, it doesn't matter in terms of being used as a dependency value for the useMemo hook. So long as the value of bookId remains the same it won't trigger a recalculation of the selected book. Since the route path params are always strings I suggest converting the value you are comparing to a String so the code uses a type-safe comparison and covers both number-like strings and alpha-numeric id values instead of coercing the bookId value to a number type.

const { books, setBooks } = useContext(BooksContext);
const { bookId = 0 } = useParams();

const selectedBook = useMemo(
  () =&gt; books.find(book =&gt; String(book.id) === bookId) || null,
  [books, bookId],
);

答案3

得分: -1

useParams hook在React Router中并不一定保证从一次渲染到另一次渲染都会返回一个持久的对象,因为它返回一个包含当前路由的URL参数的对象。

这里是一个示例:

import { useParams } from 'react-router-dom';
import { useContext, useMemo } from 'react';
import { BooksContext } from './BooksContext';

const BookDetail = () => {
  // 从上下文中检索书籍
  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;
英文:

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 &#39;react-router-dom&#39;;
import { useContext, useMemo } from &#39;react&#39;;
import { BooksContext } from &#39;./BooksContext&#39;;

const BookDetail = () =&gt; {
  // Retrieve the books from context
  const { books } = useContext(BooksContext);
  
  const { bookId = 0 } = useParams();
  
  const selectedBook = useMemo(
    () =&gt; books.find(book =&gt; book.id === +bookId) || null,
    [books, bookId]
  );

  return (
    &lt;div&gt;
      {selectedBook ? (
        &lt;div&gt;
          &lt;h1&gt;{selectedBook.title}&lt;/h1&gt;
          &lt;p&gt;{selectedBook.description}&lt;/p&gt;
        &lt;/div&gt;
      ) : (
        &lt;p&gt;No book selected.&lt;/p&gt;
      )}
    &lt;/div&gt;
  );
};

export default BookDetail;

huangapple
  • 本文由 发表于 2023年7月13日 20:33:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76679438.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定