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>
      </>
    );
  }
}