Zustand 与 Suspense 结合打破了 Nextjs 应用程序

问题描述 投票:0回答:1

当在 nextjs 应用程序中使用 zustand 启动商店并具有(不相关的)悬念块时,应用程序崩溃。

这是一个最小的复制品:https://codesandbox.io/s/holy-shape-br1uqs?file=/pages/index.js

当移除悬念块时,一切都按预期进行。

我不明白它们是如何连接的。有没有人有解决办法?

next.js zustand
1个回答
0
投票

当我运行沙箱时,错误是:

Unhandled Runtime Error
Error: This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.

这里是 9782134iouh1234 在沙箱上创建的 startTransition 示例。 https://codesandbox.io/p/sandbox/zustand-suspense-demo-dq3i7h?file=%2Fsrc%2FApp_useTransition.js

//App_useTransition.js
import {
  Suspense,
  experimental_use as use,
  useEffect,
  useReducer,
  useTransition
} from "react";
import { createStore } from "zustand";

const postStore = createStore((set) => ({
  post: fetch(`https://jsonplaceholder.typicode.com/posts/1`).then((res) =>
    res.json()
  ),
  fetchPost: (id) => {
    set({
      post: fetch(
        `https://jsonplaceholder.typicode.com/posts/${id}`
      ).then((res) => res.json())
    });
  }
}));

const usePostStore = (selector) => {
  const getValue = () => selector(postStore.getState());
  const [value, dispatch] = useReducer(getValue, undefined, getValue);
  useEffect(() => postStore.subscribe(dispatch), [selector]);
  return value;
};

const Post = () => {
  const post = use(usePostStore((state) => state.post));
  return (
    <ul>
      <li>ID: {post.id}</li>
      <li>Title: {post.title}</li>
      <li>Body: {post.body}</li>
    </ul>
  );
};

const App = () => {
  const [isPending, startTransition] = useTransition();
  const fetchPostOrig = usePostStore((state) => state.fetchPost);
  const fetchPost = (id) => {
    startTransition(() => {
      fetchPostOrig(id);
    });
  };
  return (
    <div>
      <button onClick={() => fetchPost(1)}>Fetch post 1</button>
      <button onClick={() => fetchPost(2)}>Fetch post 2</button>
      <button onClick={() => fetchPost(3)}>Fetch post 3</button>
      <button onClick={() => fetchPost(4)}>Fetch post 4</button>
      {isPending && <div>Pending...</div>}
      <hr />
      <Suspense fallback="Loading...">
        <Post />
      </Suspense>
    </div>
  );
};

export default App;
//App.js
import { Suspense, experimental_use as use } from "react";
import create from "zustand";

const usePostStore = create((set) => ({
  post: fetch(`https://jsonplaceholder.typicode.com/posts/1`).then((res) =>
    res.json()
  ),
  fetchPost: (id) => {
    set({
      post: fetch(
        `https://jsonplaceholder.typicode.com/posts/${id}`
      ).then((res) => res.json())
    });
  }
}));

const Post = () => {
  const post = use(usePostStore((state) => state.post));
  return (
    <ul>
      <li>ID: {post.id}</li>
      <li>Title: {post.title}</li>
      <li>Body: {post.body}</li>
    </ul>
  );
};

const App = () => {
  const fetchPost = usePostStore((state) => state.fetchPost);
  return (
    <div>
      <button onClick={() => fetchPost(1)}>Fetch post 1</button>
      <button onClick={() => fetchPost(2)}>Fetch post 2</button>
      <button onClick={() => fetchPost(3)}>Fetch post 3</button>
      <button onClick={() => fetchPost(4)}>Fetch post 4</button>
      <hr />
      <Suspense fallback="Loading...">
        <Post />
      </Suspense>
    </div>
  );
};

export default App;

© www.soinside.com 2019 - 2024. All rights reserved.