next/link 导航到另一个页面时丢失状态
next/link losing state when navigating to another page
我正在开发一个 库 Next.js 应用程序。出于这个问题的目的,我的应用程序中有两个页面:列出所有书籍的 BooksPage 和呈现书籍详细信息的 BookPage 。在组件方面,我有一个 <Books />
组件,它为我的图书馆数据库中的每本书呈现一个 <Book />
组件。
这是我的组件:
Books.js:
function Books({ books }) {
return (
<>
{books.map(book => (
<Book key={book.id} book={book} />
))}
</>
);
}
Book.js:
class Book extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { liked: false };
}
like = () => {
this.setState({ liked: this.state.liked ? false : true })
};
render() {
return (
<>
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
<Button onClick={this.like}>
<LikeIcon
className={this.state.liked ? "text-success" : "text-dark"}
/>
</Button>
</>
);
}
}
问题:
说我在 BooksPage 页上。当我单击 <Book />
的喜欢按钮时,图标颜色会在前端正确切换,并且在后端成功添加或删除类似内容。当我刷新 BooksPage 时,所有状态都保持一致。
当我喜欢 BooksPage 上的内容然后立即导航到 BookPage 而没有使用 next/link 刷新时,问题就出现了。那里的点赞按钮没有始终如一地切换,BooksPage 的状态丢失了。请注意,如果我硬刷新页面,一切都会恢复正常。
慢速解决方案:不要使用next/link。
替换
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
和
<a href={`/books/${book.slug}`}>{book.title}</a>
快速解决方案:继续使用 next/link?
有没有办法在导航到另一个预渲染路线时使用 next/link 并保持状态?
在 DeleteLikeView class 中,您将获得图书对象。它从数据库中检索并保存在 book 变量中。
当您删除对象时 num_of_likes 属性已在数据库中更新,但您的变量仍然包含以前的对象。在 like.delete() 命令之后,您应该再次获取对象并打印 num_of_likes att。正如你所料。
您需要使用Model.refresh_from_db(...)
--(Django Doc)方法从数据库中检索更新后的值
class DeleteLikeView(APIView):
def post(self, request, book):
book = get_object_or_404(Book, id=book)
print(book.num_of_likes)
like = Like.objects.get(user=request.user, book=book)
like.delete()
<b>book.refresh_from_db() # here is the update</b>
print(book.num_of_likes) # You will get the updated value
return ...
TLDR:任何时候需要更改按钮,API 必须更改数据,并且必须更新 最近 parent 的本地更新 button
外观的状态。 API 将控制本地状态的所有方面。除非 API 请求成功,否则您无法更新本地状态。因此,客户端和API总是1:1。
Book.js 中的 Button
组件应该 NOT 独立于 API数据;相反,无论您从 API 获取 book
数据,它还应该控制按钮的状态(及其外观)。为什么?因为使用当前方法,API 请求可能会失败,但客户端仍会更新。因此,API 和客户端可能不再是 1:1(客户端显示喜欢,但 API 仍然显示它是 disliked/unliked)。
在实践中,您将拥有一个充当状态管理器的 parent 容器。它获取所有相关数据,处理数据更新,并使用 stateless child 组件显示结果。任何时候 child 组件需要更新(例如根据按钮按下显示“喜欢”或“不喜欢”按钮),它必须首先发出 API 更改数据的请求,然后API 必须用相关数据响应以更新 child 使用的状态:
或者,如果该组件是可重用的,那么您将使用 this.props.book
(来自 parent 容器)有条件地呈现它,或者 child 组件必须从中请求数据一个 API 来更新它自己的本地 this.state.book
。与上图相同,API 请求控制状态变化的所有方面:
还有另一种方法与上图相同,但它使用 child 本地状态而不考虑 parent 的状态。这个 child 状态只会通过它自己的 class 方法更新。这引入了更多的复杂性,因为您必须确保任何 parent 状态更改不会重新呈现具有陈旧数据的 child 组件。最终,采用哪种方法取决于您。
旁注:这个问题没有太多上下文意义,因为libraries 不呈现页面,也不尝试内部页面导航。相反,他们提供可重用的组件,可由一个或多个 NextJS 项目页面或组件使用。
以某种方式通过 API 传递一本书的 liked
属性。然后,将该道具从 Books
组件向下传递到 Book
组件。
向您的图书组件添加一个 componentDidUpdate()
方法。
class Book extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { liked: this.props.liked };
}
componentDidUpdate(prevProps) {
if (this.props.liked !== prevProps.liked) {
this.setState({
liked: this.props.liked,
});
}
}
like = () => {
this.setState({ liked: !this.state.liked })
};
render() {
return (
<>
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
<Button onClick={this.like}>
<LikeIcon
className={this.state.liked ? "text-success" : "text-dark"}
/>
</Button>
</>
);
}
}
我正在开发一个 库 Next.js 应用程序。出于这个问题的目的,我的应用程序中有两个页面:列出所有书籍的 BooksPage 和呈现书籍详细信息的 BookPage 。在组件方面,我有一个 <Books />
组件,它为我的图书馆数据库中的每本书呈现一个 <Book />
组件。
这是我的组件:
Books.js:
function Books({ books }) {
return (
<>
{books.map(book => (
<Book key={book.id} book={book} />
))}
</>
);
}
Book.js:
class Book extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { liked: false };
}
like = () => {
this.setState({ liked: this.state.liked ? false : true })
};
render() {
return (
<>
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
<Button onClick={this.like}>
<LikeIcon
className={this.state.liked ? "text-success" : "text-dark"}
/>
</Button>
</>
);
}
}
问题:
说我在 BooksPage 页上。当我单击 <Book />
的喜欢按钮时,图标颜色会在前端正确切换,并且在后端成功添加或删除类似内容。当我刷新 BooksPage 时,所有状态都保持一致。
当我喜欢 BooksPage 上的内容然后立即导航到 BookPage 而没有使用 next/link 刷新时,问题就出现了。那里的点赞按钮没有始终如一地切换,BooksPage 的状态丢失了。请注意,如果我硬刷新页面,一切都会恢复正常。
慢速解决方案:不要使用next/link。
替换
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
和
<a href={`/books/${book.slug}`}>{book.title}</a>
快速解决方案:继续使用 next/link?
有没有办法在导航到另一个预渲染路线时使用 next/link 并保持状态?
在 DeleteLikeView class 中,您将获得图书对象。它从数据库中检索并保存在 book 变量中。 当您删除对象时 num_of_likes 属性已在数据库中更新,但您的变量仍然包含以前的对象。在 like.delete() 命令之后,您应该再次获取对象并打印 num_of_likes att。正如你所料。
您需要使用Model.refresh_from_db(...)
--(Django Doc)方法从数据库中检索更新后的值
class DeleteLikeView(APIView):
def post(self, request, book):
book = get_object_or_404(Book, id=book)
print(book.num_of_likes)
like = Like.objects.get(user=request.user, book=book)
like.delete()
<b>book.refresh_from_db() # here is the update</b>
print(book.num_of_likes) # You will get the updated value
return ...
TLDR:任何时候需要更改按钮,API 必须更改数据,并且必须更新 最近 parent 的本地更新 button
外观的状态。 API 将控制本地状态的所有方面。除非 API 请求成功,否则您无法更新本地状态。因此,客户端和API总是1:1。
Book.js 中的 Button
组件应该 NOT 独立于 API数据;相反,无论您从 API 获取 book
数据,它还应该控制按钮的状态(及其外观)。为什么?因为使用当前方法,API 请求可能会失败,但客户端仍会更新。因此,API 和客户端可能不再是 1:1(客户端显示喜欢,但 API 仍然显示它是 disliked/unliked)。
在实践中,您将拥有一个充当状态管理器的 parent 容器。它获取所有相关数据,处理数据更新,并使用 stateless child 组件显示结果。任何时候 child 组件需要更新(例如根据按钮按下显示“喜欢”或“不喜欢”按钮),它必须首先发出 API 更改数据的请求,然后API 必须用相关数据响应以更新 child 使用的状态:
或者,如果该组件是可重用的,那么您将使用 this.props.book
(来自 parent 容器)有条件地呈现它,或者 child 组件必须从中请求数据一个 API 来更新它自己的本地 this.state.book
。与上图相同,API 请求控制状态变化的所有方面:
还有另一种方法与上图相同,但它使用 child 本地状态而不考虑 parent 的状态。这个 child 状态只会通过它自己的 class 方法更新。这引入了更多的复杂性,因为您必须确保任何 parent 状态更改不会重新呈现具有陈旧数据的 child 组件。最终,采用哪种方法取决于您。
旁注:这个问题没有太多上下文意义,因为libraries 不呈现页面,也不尝试内部页面导航。相反,他们提供可重用的组件,可由一个或多个 NextJS 项目页面或组件使用。
以某种方式通过 API 传递一本书的 liked
属性。然后,将该道具从 Books
组件向下传递到 Book
组件。
向您的图书组件添加一个 componentDidUpdate()
方法。
class Book extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { liked: this.props.liked };
}
componentDidUpdate(prevProps) {
if (this.props.liked !== prevProps.liked) {
this.setState({
liked: this.props.liked,
});
}
}
like = () => {
this.setState({ liked: !this.state.liked })
};
render() {
return (
<>
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
<Button onClick={this.like}>
<LikeIcon
className={this.state.liked ? "text-success" : "text-dark"}
/>
</Button>
</>
);
}
}