英文:
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
短期内来看,不会返回一个持久的对象引用,而是在每次渲染周期中返回一个新的对象引用。如果你愿意,这很容易测试:
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:
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 = "foo";
const b = a;
const c = "foo";
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(
() => books.find(book => 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 '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;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论