@@ -1067,7 +1067,7 @@ export const getMessage = async () => {
)
}
-`,ex=n=>{const e=unescape(n.message.replace(/\\u/gi,"%u")),s=unescape((n.title||"").replace(/\\u/gi,"%u"));return t.jsxs("div",{className:"flex border border-blue-100 p-4 rounded-md shadow",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:s||"Heads Up!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]})},C4=()=>new Promise(n=>{n({value:"React does not preserve any state for renders that got suspended before they were able to mount for the first time. When the component has loaded, React will retry rendering the suspended tree from scratch."})});function R4(){const n=C4();return t.jsx(h.Suspense,{fallback:"",children:t.jsx(E4,{promise:n})})}function E4(n){const{value:e}=h.use(n.promise);return t.jsx(ex,{message:e})}document.documentElement.style.fontSize="14px";function M4(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:c,success:i,...o}=n,p=Pe(se("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":i,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":c,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,c&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}function vu(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}const A4=["React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.","Whether you work on your own or with thousands of other developers, using React feels the same.","React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.","This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ","You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it."];var nx=new Headers;nx.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var T4={method:"GET",headers:nx,redirect:"follow",data:"hex"};const $4="https://echo.apifox.com/delay/1",tx=async()=>{await fetch($4,T4);const n=Math.floor(Math.random()*10)%5;return{value:A4[n]}};function _m(){const n=tx();return t.jsx(h.Suspense,{fallback:t.jsx(vu,{}),children:t.jsx(D4,{promise:n})})}const D4=n=>{const e=h.use(n.promise);return t.jsxs("div",{className:"flex border border-blue-100 p-4 rounded-md shadow",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:"Heads Up!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e.value})]})]})};function L4(){return t.jsx(h.Suspense,{fallback:t.jsx(vu,{}),children:t.jsx(ex,{message:"这是一个普通的 UI 组件,Skeleton 组件不会有任何渲染机会,直接渲染 Message 组件",title:"Primary"})})}function km(n){const e={blockquote:"blockquote",code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.h2,{children:t.jsx(e.em,{children:"0"})}),`
+`,rx=n=>{const e=unescape(n.message.replace(/\\u/gi,"%u")),s=unescape((n.title||"").replace(/\\u/gi,"%u"));return t.jsxs("div",{className:"flex border border-blue-100 p-4 rounded-md shadow",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:s||"Heads Up!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]})},X4=()=>new Promise(n=>{n({value:"React does not preserve any state for renders that got suspended before they were able to mount for the first time. When the component has loaded, React will retry rendering the suspended tree from scratch."})});function G4(){const n=X4();return t.jsx(f.Suspense,{fallback:"",children:t.jsx(Z4,{promise:n})})}function Z4(n){const{value:e}=f.use(n.promise);return t.jsx(rx,{message:e})}document.documentElement.style.fontSize="14px";function Y4(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:i,success:c,...o}=n,p=Ke(ae("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":c,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":i,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,i&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}function ju(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}const Q4=["React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.","Whether you work on your own or with thousands of other developers, using React feels the same.","React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.","This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ","You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it."];var ix=new Headers;ix.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var J4={method:"GET",headers:ix,redirect:"follow",data:"hex"};const P4="https://echo.apifox.com/delay/1",cx=async()=>{await fetch(P4,J4);const n=Math.floor(Math.random()*10)%5;return{value:Q4[n]}};function wm(){const n=cx();return t.jsx(f.Suspense,{fallback:t.jsx(ju,{}),children:t.jsx(W4,{promise:n})})}const W4=n=>{const e=f.use(n.promise);return t.jsxs("div",{className:"flex border border-blue-100 p-4 rounded-md shadow",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:"Heads Up!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e.value})]})]})};function K4(){return t.jsx(f.Suspense,{fallback:t.jsx(ju,{}),children:t.jsx(rx,{message:"这是一个普通的 UI 组件,Skeleton 组件不会有任何渲染机会,直接渲染 Message 组件",title:"Primary"})})}function _m(n){const e={blockquote:"blockquote",code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.h2,{children:t.jsx(e.em,{children:"0"})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"Suspense"})}),`
`,t.jsx(e.p,{children:"先来回顾一下这段代码。在前面一篇文中,我们知道,如果直接使用 use 获取未直接创建的 Promise 中的值,会抛出一个异常。"}),`
`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`const _api3 = () => {
@@ -1107,7 +1107,7 @@ function Content(props) {
}
`})}),`
`,t.jsxs(e.p,{children:["在这段代码中,为了让 Suspense 捕获更小范围的组件,我们单独定义了一个子组件 ",t.jsx(e.code,{children:"Content"})," 来使用 use 获取 promise 中的数据。这也是未来使用的比较常规的思路和手段。案例预览效果如下所示。"]}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(R4,{})}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(G4,{})}),`
`,t.jsx(e.p,{children:"当然,在开发中更常见的效果是使用 use 读取异步 promise,主要的场景就是接口请求。"}),`
`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-html",children:`
}>
@@ -1151,7 +1151,7 @@ function Content(props) {
`,t.jsxs(e.p,{children:["当 Message 组件首次渲染时,由于直接读取 promise 导致报错,Suspense 捕获到该异常后,会渲染 ",t.jsx(e.code,{children:"fallback"})," 中设置的组件。此时我们设置了一个骨架屏 Skeleton 组件,大家可以多次点击 reload 按钮查看演示效果。"]}),`
`,t.jsx(e.p,{children:"因此,这个案例的视觉表现应该为:1. 首先渲染 Skeleton 组件。然后请求成功之后,use 渲染 Message 组件。"}),`
`,t.jsx(e.p,{children:"演示案例运行结果如下"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(_m,{})}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(wm,{})}),`
`,t.jsx(e.h2,{children:t.jsx(e.em,{children:"1"})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"Suspense 工作原理"})}),`
`,t.jsxs(e.p,{children:["Suspense 提供了一个加载数据的标准。在源码中,Suspense 的子组件被称为 ",t.jsx(e.code,{children:"primary"}),"。"]}),`
@@ -1187,7 +1187,7 @@ export default function Index() {
}
`})}),`
`,t.jsx(e.p,{children:"渲染结果如下"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(L4,{})}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(K4,{})}),`
`,t.jsx(e.h2,{children:t.jsx(e.em,{children:"3"})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"新旧实现对比"})}),`
`,t.jsx(e.p,{children:"在前面我们 结合 use 与 Suspense 实现了一个初始化加载的案例。该案例的视觉表现是在初始化时,首先显示 Skeleton 组件,请求成功之后,显示 Message 组件。"}),`
@@ -1202,7 +1202,7 @@ export default function Index() {
}
`})}),`
`,t.jsx(e.p,{children:"刷新页面时重新请求数据渲染,请求过程中显示骨架屏组件 Skeleton"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(_m,{})}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(wm,{})}),`
`,t.jsxs(e.p,{children:["这里我们需要关注的是,对比以前必须要借助 ",t.jsx(e.code,{children:"state"})," ",t.jsx(e.code,{children:"useEffect"})," 的实现方式,体会一下差别。核心逻辑如下"]}),`
`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-javascript",children:`// 之前的实现方式
export default function Index() {
@@ -1262,7 +1262,7 @@ export default function Index() {
`,t.jsxs(e.blockquote,{children:[`
`,t.jsx(e.p,{children:"Vue3 也是这种类似自定义 hook 的方式。但是这两种开发方式是有本质区别的。"}),`
`]}),`
-`,t.jsx(e.p,{children:"这是我们之前版本的最佳实践。注意体会他们之间的区别。相似,但却不同。我们后续会列举更多案例,尽可能用新的开发思路去复现开发过程中会出现的场景。"})]})}function B4(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(km,{...n})}):km(n)}function O4(){const[n,e]=h.useState({value:""}),[s,a]=h.useState(!0);return h.useEffect(()=>{tx().then(l=>{e(l),a(!1)})},[]),s?t.jsx(vu,{}):t.jsxs(t.Fragment,{children:[t.jsx(z4,{message:n.value}),t.jsx("div",{className:"mt-8 text-center",children:t.jsx(M4,{signal:!0,children:"点击下方刷新按钮可重置演示"})})]})}const z4=n=>{const e=n.message;return t.jsxs("div",{className:"flex border border-blue-100 p-4 rounded-md shadow",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:"a new message!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]})},U4={"App.js":S4,"api.js":_4,"Button.jsx":w4,"Skeleton.jsx":k4};function H4(){return t.jsx(ae,{files:U4,renderArticle:n=>t.jsx(B4,{components:{code:n}}),caseRender:t.jsx(O4,{})})}const q4=`import {use, useState, Suspense} from 'react'
+`,t.jsx(e.p,{children:"这是我们之前版本的最佳实践。注意体会他们之间的区别。相似,但却不同。我们后续会列举更多案例,尽可能用新的开发思路去复现开发过程中会出现的场景。"})]})}function I4(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(_m,{...n})}):_m(n)}function F4(){const[n,e]=f.useState({value:""}),[s,a]=f.useState(!0);return f.useEffect(()=>{cx().then(l=>{e(l),a(!1)})},[]),s?t.jsx(ju,{}):t.jsxs(t.Fragment,{children:[t.jsx(ey,{message:n.value}),t.jsx("div",{className:"mt-8 text-center",children:t.jsx(Y4,{signal:!0,children:"点击下方刷新按钮可重置演示"})})]})}const ey=n=>{const e=n.message;return t.jsxs("div",{className:"flex border border-blue-100 p-4 rounded-md shadow",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:"a new message!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]})},ny={"App.js":U4,"api.js":q4,"Button.jsx":H4,"Skeleton.jsx":V4};function ty(){return t.jsx(W,{files:ny,renderArticle:n=>t.jsx(I4,{components:{code:n}}),caseRender:t.jsx(F4,{})})}const sy=`import {use, useState, Suspense} from 'react'
import Message from './Message'
import Skeleton from './Skeleton'
import Button from './Button'
@@ -1297,7 +1297,7 @@ function Content(props) {
)
}
-`,V4=`import {twMerge} from 'tailwind-merge'
+`,ay=`import {twMerge} from 'tailwind-merge'
import clsx from 'clsx'
document.documentElement.style.fontSize = '14px'
@@ -1337,7 +1337,7 @@ export default function Button(props) {
)
}
-`,X4=`const random = [
+`,ly=`const random = [
'React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.',
'Whether you work on your own or with thousands of other developers, using React feels the same.',
'React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.',
@@ -1365,7 +1365,7 @@ export const getMessage = async () => {
value: random[i]
}
}
-`,G4=`export default function Skeleton(props) {
+`,ry=`export default function Skeleton(props) {
return (
@@ -1384,7 +1384,7 @@ export const getMessage = async () => {
)
}
-`,Z4=`const Message = (props) => {
+`,iy=`const Message = (props) => {
const message = unescape(props.message.replace(/\\\\u/gi,'%u'));
const title = unescape((props.title || '').replace(/\\\\u/gi,'%u'));
@@ -1417,10 +1417,10 @@ export const getMessage = async () => {
}
export default Message
-`,rl=n=>{const e=unescape(n.message.replace(/\\u/gi,"%u")),s=unescape((n.title||"").replace(/\\u/gi,"%u"));let a="flex border border-blue-100 p-4 rounded-md shadow";return e?t.jsxs("div",{className:a,children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:s||"Heads Up!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]}):t.jsxs("div",{className:`${a} justify-center items-center flex-col text-gray-500`,children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z"})}),t.jsx("div",{className:"mt-2",children:"no data."})]})};function ju(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}document.documentElement.style.fontSize="14px";function bu(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:c,success:i,...o}=n,p=Pe(se("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":i,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":c,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,c&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}const Y4=["React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.","Whether you work on your own or with thousands of other developers, using React feels the same.","React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.","This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ","You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it."];var sx=new Headers;sx.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var Q4={method:"GET",headers:sx,redirect:"follow",data:"hex"};const J4="https://echo.apifox.com/delay/1",yu=async()=>{await fetch(J4,Q4);const n=Math.floor(Math.random()*10)%5;return{value:Y4[n]}};function K4(){const[n,e]=h.useState(null);function s(){e(yu())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(bu,{onClick:s,children:"更新数据"})}),t.jsx(h.Suspense,{fallback:t.jsx(ju,{}),children:t.jsx(W4,{promise:n})})]})}function W4(n){if(!n.promise)return t.jsx(rl,{message:""});const{value:e}=h.use(n.promise);return t.jsx(rl,{message:e})}function P4(){const[n,e]=h.useState(Promise.resolve({value:""}));function s(){e(yu())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(bu,{onClick:s,children:"更新数据"})}),t.jsx(h.Suspense,{fallback:t.jsx(ju,{}),children:t.jsx(F4,{promise:n})})]})}function F4(n){const{value:e}=h.use(n.promise);return t.jsx(rl,{message:e})}function Cm(n){const e={blockquote:"blockquote",code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"接下来,我们将会以大量的实践案例来展开 React 19 新 hook 的运用。"}),`
+`,il=n=>{const e=unescape(n.message.replace(/\\u/gi,"%u")),s=unescape((n.title||"").replace(/\\u/gi,"%u"));let a="flex border border-blue-100 p-4 rounded-md shadow";return e?t.jsxs("div",{className:a,children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:s||"Heads Up!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]}):t.jsxs("div",{className:`${a} justify-center items-center flex-col text-gray-500`,children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z"})}),t.jsx("div",{className:"mt-2",children:"no data."})]})};function vu(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}document.documentElement.style.fontSize="14px";function bu(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:i,success:c,...o}=n,p=Ke(ae("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":c,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":i,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,i&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}const cy=["React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.","Whether you work on your own or with thousands of other developers, using React feels the same.","React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.","This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ","You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it."];var ox=new Headers;ox.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var oy={method:"GET",headers:ox,redirect:"follow",data:"hex"};const uy="https://echo.apifox.com/delay/1",yu=async()=>{await fetch(uy,oy);const n=Math.floor(Math.random()*10)%5;return{value:cy[n]}};function dy(){const[n,e]=f.useState(null);function s(){e(yu())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(bu,{onClick:s,children:"更新数据"})}),t.jsx(f.Suspense,{fallback:t.jsx(vu,{}),children:t.jsx(my,{promise:n})})]})}function my(n){if(!n.promise)return t.jsx(il,{message:""});const{value:e}=f.use(n.promise);return t.jsx(il,{message:e})}function py(){const[n,e]=f.useState(Promise.resolve({value:""}));function s(){e(yu())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(bu,{onClick:s,children:"更新数据"})}),t.jsx(f.Suspense,{fallback:t.jsx(vu,{}),children:t.jsx(fy,{promise:n})})]})}function fy(n){const{value:e}=f.use(n.promise);return t.jsx(il,{message:e})}function km(n){const e={blockquote:"blockquote",code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"接下来,我们将会以大量的实践案例来展开 React 19 新 hook 的运用。"}),`
`,t.jsxs(e.p,{children:["本文模拟的实践案例为",t.jsx(e.strong,{children:"点击按钮更新数据"}),"。这在开发中是一个非常常见的场景。"]}),`
`,t.jsx(e.p,{children:"案例完成之后的最终演示效果图如下。案例的视觉表现为:初始化时没有请求,所以组件显示为空数据样式。当我们点击按钮时请求一条数据,数据更新,请求成功之后显示更新之后的内容。"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(K4,{})}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(dy,{})}),`
`,t.jsx(e.p,{children:"接下来,我们直接用 React 19 新的开发方式来完成这个需求。"}),`
`,t.jsx(e.h2,{children:t.jsx(e.em,{children:"0"})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"基础实现"})}),`
@@ -1532,8 +1532,8 @@ const [promise, update] = useState(null)
}
`})}),`
`,t.jsx(e.p,{children:"演示效果如下图所示"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(P4,{})}),`
-`,t.jsx(e.p,{children:"这种写法有一个很小的瑕疵,就是在初始化时,也不可避免的显示了 Skeleton 组件,实际上是不需要的。因此具体采用哪种写法,要依据实践中的需求而定。"})]})}function I4(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Cm,{...n})}):Cm(n)}function e3(){const[n,e]=h.useState(null);function s(){e(yu())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(bu,{onClick:s,children:"更新数据"})}),t.jsx(h.Suspense,{fallback:t.jsx(ju,{}),children:t.jsx(n3,{promise:n})})]})}function n3(n){if(!n.promise)return t.jsx(rl,{message:""});const{value:e}=h.use(n.promise);return t.jsx(rl,{message:e})}const t3={"App.js":q4,"api.js":X4,"Message.jsx":Z4,"Button.jsx":V4,"Skeleton.jsx":G4};function s3(){return t.jsx(ae,{files:t3,renderArticle:n=>t.jsx(I4,{components:{code:n}}),caseRender:t.jsx(e3,{})})}const a3=`import {use, useState, Suspense} from 'react'
+`,t.jsx(A,{reload:!0,children:t.jsx(py,{})}),`
+`,t.jsx(e.p,{children:"这种写法有一个很小的瑕疵,就是在初始化时,也不可避免的显示了 Skeleton 组件,实际上是不需要的。因此具体采用哪种写法,要依据实践中的需求而定。"})]})}function hy(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(km,{...n})}):km(n)}function xy(){const[n,e]=f.useState(null);function s(){e(yu())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(bu,{onClick:s,children:"更新数据"})}),t.jsx(f.Suspense,{fallback:t.jsx(vu,{}),children:t.jsx(gy,{promise:n})})]})}function gy(n){if(!n.promise)return t.jsx(il,{message:""});const{value:e}=f.use(n.promise);return t.jsx(il,{message:e})}const jy={"App.js":sy,"api.js":ly,"Message.jsx":iy,"Button.jsx":ay,"Skeleton.jsx":ry};function vy(){return t.jsx(W,{files:jy,renderArticle:n=>t.jsx(hy,{components:{code:n}}),caseRender:t.jsx(xy,{})})}const by=`import {use, useState, Suspense} from 'react'
import Message from './Message'
import Skeleton from './Skeleton'
import Button from './Button'
@@ -1564,7 +1564,7 @@ function Content(props) {
)
}
-`,l3=`import {twMerge} from 'tailwind-merge'
+`,yy=`import {twMerge} from 'tailwind-merge'
import clsx from 'clsx'
document.documentElement.style.fontSize = '14px'
@@ -1604,7 +1604,7 @@ export default function Button(props) {
)
}
-`,r3=`const random = [
+`,Ny=`const random = [
'React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.',
'Whether you work on your own or with thousands of other developers, using React feels the same.',
'React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.',
@@ -1632,7 +1632,7 @@ export const getMessage = async () => {
value: random[i]
}
}
-`,c3=`export default function Skeleton(props) {
+`,Sy=`export default function Skeleton(props) {
return (
@@ -1651,7 +1651,7 @@ export const getMessage = async () => {
)
}
-`,i3=`const Message = (props) => {
+`,wy=`const Message = (props) => {
const message = unescape(props.message.replace(/\\\\u/gi,'%u'));
const title = unescape((props.title || '').replace(/\\\\u/gi,'%u'));
@@ -1684,7 +1684,7 @@ export const getMessage = async () => {
}
export default Message
-`,Nu=n=>{const e=unescape(n.message.replace(/\\u/gi,"%u")),s=unescape((n.title||"").replace(/\\u/gi,"%u"));let a="flex border border-blue-100 p-4 rounded-md shadow";return e?t.jsxs("div",{className:a,children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:s||"Heads Up!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]}):t.jsxs("div",{className:`${a} justify-center items-center flex-col text-gray-500`,children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z"})}),t.jsx("div",{className:"mt-2",children:"no data."})]})};function Su(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}document.documentElement.style.fontSize="14px";function wu(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:c,success:i,...o}=n,p=Pe(se("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":i,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":c,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,c&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}const o3=["React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.","Whether you work on your own or with thousands of other developers, using React feels the same.","React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.","This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ","You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it."];var ax=new Headers;ax.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var u3={method:"GET",headers:ax,redirect:"follow",data:"hex"};const d3="https://echo.apifox.com/delay/1",Js=async()=>{await fetch(d3,u3);const n=Math.floor(Math.random()*10)%5;return{value:o3[n]}};function m3(){const[n,e]=h.useState({value:""}),[s,a]=h.useState(!0);h.useEffect(()=>{Js().then(r=>{e(r),a(!1)})},[]);function l(){a(!0),Js().then(r=>{e(r),a(!1)})}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(wu,{onClick:l,children:"更新数据"})}),s?t.jsx(Su,{}):t.jsx(Nu,{message:n.value})]})}function p3(){const[n,e]=h.useState(Js());function s(){e(Js())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(wu,{onClick:s,children:"更新数据"})}),t.jsx(h.Suspense,{fallback:t.jsx(Su,{}),children:t.jsx(f3,{promise:n})})]})}function f3(n){const{value:e}=h.use(n.promise);return t.jsx(Nu,{message:e})}function Rm(n){const e={blockquote:"blockquote",code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsxs(e.p,{children:["在上一章的基础之上,我们做一个小的需求变动。上一章的案例要求我们不要初始化时请求一条数据,因此,默认渲染结果是 ",t.jsx(e.code,{children:"no data"})]}),`
+`,Nu=n=>{const e=unescape(n.message.replace(/\\u/gi,"%u")),s=unescape((n.title||"").replace(/\\u/gi,"%u"));let a="flex border border-blue-100 p-4 rounded-md shadow";return e?t.jsxs("div",{className:a,children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6 mt-2",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"})}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:s||"Heads Up!"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]}):t.jsxs("div",{className:`${a} justify-center items-center flex-col text-gray-500`,children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-6",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z"})}),t.jsx("div",{className:"mt-2",children:"no data."})]})};function Su(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}document.documentElement.style.fontSize="14px";function wu(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:i,success:c,...o}=n,p=Ke(ae("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":c,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":i,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,i&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}const _y=["React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.","Whether you work on your own or with thousands of other developers, using React feels the same.","React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.","This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ","You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it."];var ux=new Headers;ux.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var ky={method:"GET",headers:ux,redirect:"follow",data:"hex"};const Cy="https://echo.apifox.com/delay/1",Ps=async()=>{await fetch(Cy,ky);const n=Math.floor(Math.random()*10)%5;return{value:_y[n]}};function Ry(){const[n,e]=f.useState({value:""}),[s,a]=f.useState(!0);f.useEffect(()=>{Ps().then(r=>{e(r),a(!1)})},[]);function l(){a(!0),Ps().then(r=>{e(r),a(!1)})}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(wu,{onClick:l,children:"更新数据"})}),s?t.jsx(Su,{}):t.jsx(Nu,{message:n.value})]})}function My(){const[n,e]=f.useState(Ps());function s(){e(Ps())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(wu,{onClick:s,children:"更新数据"})}),t.jsx(f.Suspense,{fallback:t.jsx(Su,{}),children:t.jsx(Ey,{promise:n})})]})}function Ey(n){const{value:e}=f.use(n.promise);return t.jsx(Nu,{message:e})}function Cm(n){const e={blockquote:"blockquote",code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsxs(e.p,{children:["在上一章的基础之上,我们做一个小的需求变动。上一章的案例要求我们不要初始化时请求一条数据,因此,默认渲染结果是 ",t.jsx(e.code,{children:"no data"})]}),`
`,t.jsx(e.p,{children:"这一章的案例则不同,我们需要在初始化时请求数据。也就是说,我们此时同时需要初始化和更新的逻辑"}),`
`,t.jsx(e.h2,{children:t.jsx(e.em,{children:"0"})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"需求变动之后的思考"})}),`
@@ -1747,7 +1747,7 @@ export default function Demo01() {
}
`})}),`
`,t.jsx(e.p,{children:"演示效果如下案例所示"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(m3,{})}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(Ry,{})}),`
`,t.jsx(e.h2,{children:t.jsx(e.em,{children:"1"})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"新的实现方式"})}),`
`,t.jsx(e.p,{children:"与之前版本的实现方式相比,新的开发方式就简单了许多。"}),`
@@ -1790,7 +1790,7 @@ function Content(props) {
}
`})}),`
`,t.jsx(e.p,{children:"演示效果如下所示"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(p3,{})}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(My,{})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"非常的方便省事。"})}),`
`,t.jsxs(e.p,{children:["最后需要注意的是一个小的细节,如果不考虑 Compiler 编译之后的代码去缓存初始化时的 ",t.jsx(e.code,{children:"getMessage()"}),",那么每次更新组件时,该方法都会执行一次,因此,会导致冗余的接口请求。"]}),`
`,t.jsxs(e.blockquote,{children:[`
@@ -1800,7 +1800,7 @@ function Content(props) {
`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-diff",children:`- const [promise, update] = useState(getMessage())
+ const [promise, update] = useState(getMessage)
`})}),`
-`,t.jsx(e.p,{children:"这样,即使不用 Compiler 编译缓存,也不会出现冗余请求的情况。完整的代码和演示案例请在右侧模块中查看。"})]})}function h3(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Rm,{...n})}):Rm(n)}function x3(){const[n,e]=h.useState(Js);function s(){e(Js())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(wu,{onClick:s,children:"更新数据"})}),t.jsx(h.Suspense,{fallback:t.jsx(Su,{}),children:t.jsx(g3,{promise:n})})]})}function g3(n){const{value:e}=h.use(n.promise);return t.jsx(Nu,{message:e})}const v3={"App.js":a3,"api.js":r3,"Message.jsx":i3,"Button.jsx":l3,"Skeleton.jsx":c3};function j3(){return t.jsx(ae,{files:v3,renderArticle:n=>t.jsx(h3,{components:{code:n}}),caseRender:t.jsx(x3,{})})}const b3=`import {use, useState, Suspense, useEffect} from 'react'
+`,t.jsx(e.p,{children:"这样,即使不用 Compiler 编译缓存,也不会出现冗余请求的情况。完整的代码和演示案例请在右侧模块中查看。"})]})}function Ay(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Cm,{...n})}):Cm(n)}function dx(){const[n,e]=f.useState(Ps);function s(){e(Ps())}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(wu,{onClick:s,children:"更新数据"})}),t.jsx(f.Suspense,{fallback:t.jsx(Su,{}),children:t.jsx(Ty,{promise:n})})]})}function Ty(n){const{value:e}=f.use(n.promise);return t.jsx(Nu,{message:e})}const $y={"App.js":by,"api.js":Ny,"Message.jsx":wy,"Button.jsx":yy,"Skeleton.jsx":Sy};function Ly(){return t.jsx(W,{files:$y,renderArticle:n=>t.jsx(Ay,{components:{code:n}}),caseRender:t.jsx(dx,{})})}const Dy=`import {use, useState, Suspense, useEffect} from 'react'
import Userinfo from './Userinfo'
import Skeleton from './Skeleton'
import Button from './Button'
@@ -1833,7 +1833,7 @@ function User(props) {
)
}
-`,y3=`import {twMerge} from 'tailwind-merge'
+`,Oy=`import {twMerge} from 'tailwind-merge'
import clsx from 'clsx'
document.documentElement.style.fontSize = '14px'
@@ -1873,7 +1873,7 @@ export default function Button(props) {
)
}
-`,N3=`const random = [
+`,By=`const random = [
'React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.',
'Whether you work on your own or with thousands of other developers, using React feels the same.',
'React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.',
@@ -1914,7 +1914,7 @@ function getUuid() {
let uuid = s.join("")
return uuid
}
-`,S3=`export default function Skeleton(props) {
+`,zy=`export default function Skeleton(props) {
return (
@@ -1933,7 +1933,7 @@ function getUuid() {
)
}
-`,w3=`const Userinfo = (props) => {
+`,Uy=`const Userinfo = (props) => {
const message = unescape(props.message.replace(/\\\\u/gi,'%u'));
const username = unescape((props.username || '').replace(/\\\\u/gi,'%u'));
const index = props.index || 0;
@@ -1952,7 +1952,7 @@ function getUuid() {
}
export default Userinfo
-`,lx=n=>{const e=unescape(n.message.replace(/\\u/gi,"%u")),s=unescape((n.username||"").replace(/\\u/gi,"%u")),a=n.index||0;return t.jsxs("div",{className:"flex border border-blue-100 p-4 rounded-md my-4 items-start",children:[t.jsx("img",{className:"w-10",src:`https://api.dicebear.com/7.x/miniavs/svg?seed=${a}`,alt:""}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:s||"no name"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]})};function rx(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}document.documentElement.style.fontSize="14px";function cx(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:c,success:i,...o}=n,p=Pe(se("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":i,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":c,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,c&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}const _3=["React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.","Whether you work on your own or with thousands of other developers, using React feels the same.","React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.","This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ","You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it."];var ix=new Headers;ix.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var k3={method:"GET",headers:ix,redirect:"follow",data:"hex"};const C3="https://echo.apifox.com/delay/1",Dr=async()=>{await fetch(C3,k3);const n=Math.floor(Math.random()*10)%5;return{value:_3[n],id:R3()}};function R3(){for(var n=[],e="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",s=0;s<16;s++)n[s]=e.substr(Math.floor(Math.random()*16),1);return n[8]="-",n.join("")}function E3(){const[n,e]=h.useState([]);h.useEffect(()=>{e([...n,{type:"loading"}]),Dr().then(a=>{e([...n,a])})},[]);function s(){e([...n,{type:"loading"}]),Dr().then(a=>{e([...n,a])})}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(cx,{onClick:s,children:"新增数据"})}),t.jsx(M3,{list:n})]})}function M3(n){const e=n.list;return t.jsx(t.Fragment,{children:e.map((s,a)=>s.type==="loading"?t.jsx(rx,{},`hello ${a}`):t.jsx(lx,{index:a,username:s.id,message:s.value},`h${a}`))})}function Em(n){const e={code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"这一章我们要学习的是一个新增加载项到列表的案例。首先我们会通过接口获取到一条数据,然后将该数据渲染到列表中。你可以先通过右侧预览查看最终演示效果。"}),`
+`,mx=n=>{const e=unescape(n.message.replace(/\\u/gi,"%u")),s=unescape((n.username||"").replace(/\\u/gi,"%u")),a=n.index||0;return t.jsxs("div",{className:"flex border border-blue-100 p-4 rounded-md my-4 items-start",children:[t.jsx("img",{className:"w-10",src:`https://api.dicebear.com/7.x/miniavs/svg?seed=${a}`,alt:""}),t.jsxs("div",{className:"flex-1 ml-3",children:[t.jsx("div",{children:s||"no name"}),t.jsx("div",{className:"text-sm mt-1 text-gray-600",children:e})]})]})};function px(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}document.documentElement.style.fontSize="14px";function fx(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:i,success:c,...o}=n,p=Ke(ae("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":c,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":i,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,i&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}const Hy=["React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.","Whether you work on your own or with thousands of other developers, using React feels the same.","React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.","This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ","You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it."];var hx=new Headers;hx.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var qy={method:"GET",headers:hx,redirect:"follow",data:"hex"};const Vy="https://echo.apifox.com/delay/1",Dr=async()=>{await fetch(Vy,qy);const n=Math.floor(Math.random()*10)%5;return{value:Hy[n],id:Xy()}};function Xy(){for(var n=[],e="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",s=0;s<16;s++)n[s]=e.substr(Math.floor(Math.random()*16),1);return n[8]="-",n.join("")}function Gy(){const[n,e]=f.useState([]);f.useEffect(()=>{e([...n,{type:"loading"}]),Dr().then(a=>{e([...n,a])})},[]);function s(){e([...n,{type:"loading"}]),Dr().then(a=>{e([...n,a])})}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(fx,{onClick:s,children:"新增数据"})}),t.jsx(Zy,{list:n})]})}function Zy(n){const e=n.list;return t.jsx(t.Fragment,{children:e.map((s,a)=>s.type==="loading"?t.jsx(px,{},`hello ${a}`):t.jsx(mx,{index:a,username:s.id,message:s.value},`h${a}`))})}function Rm(n){const e={code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"这一章我们要学习的是一个新增加载项到列表的案例。首先我们会通过接口获取到一条数据,然后将该数据渲染到列表中。你可以先通过右侧预览查看最终演示效果。"}),`
`,t.jsx(e.h2,{children:t.jsx(e.em,{children:"0"})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"使用旧的方案实现"})}),`
`,t.jsx(e.p,{children:"首先,先定义请求数据的 promise"}),`
@@ -2040,7 +2040,7 @@ function List(props) {
}
`})}),`
`,t.jsx(e.p,{children:"案例演示效果如下所示"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(E3,{})}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(Gy,{})}),`
`,t.jsx(e.h2,{children:t.jsx(e.em,{children:"1"})}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"新的思路"})}),`
`,t.jsx(e.p,{children:"旧的思路在实现上非常巧妙。但是简洁度依然弱于新的实现方案。除此之外,旧的实现思路还有许多问题需要处理,例如初始化时请求了两次,我们要考虑接口防重的问题。以及当我们多次连续点击按钮时,会出现竞态问题而导致渲染结果出现混乱。"}),`
@@ -2057,7 +2057,7 @@ function List(props) {
`,t.jsx(e.p,{children:"处理好之后,我们只需要使用 map 遍历该数组即可。在遍历逻辑中,每一项都返回 Suspense 包裹的子组件。我们将 promise 传递给该子组件,并在子组件中使用 use 读取 promise 中的值。"}),`
`,t.jsx(e.p,{children:"最终的完整代码与演示结果请在右侧案例中查看。"}),`
`,t.jsx(e.p,{children:"此时通过案例演示结果可以观察到,初始化时的接口重复问题被解决掉了,并且当我们多次连续点击新增时,也不会出现接口竞态混乱的问题。"}),`
-`,t.jsx(e.p,{children:"希望大家能够通过这个案例,进一步感受到新的开发思维的强大之处。"})]})}function A3(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Em,{...n})}):Em(n)}function T3(){const[n,e]=h.useState(()=>[Dr()]);function s(){e([...n,Dr()])}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(cx,{onClick:s,children:"新增数据"})}),n.map((a,l)=>t.jsx(h.Suspense,{fallback:t.jsx(rx,{}),children:t.jsx($3,{promise:a,index:l})},`hello ${l}`))]})}function $3(n){const e=h.use(n.promise);return t.jsx(lx,{index:n.index,username:e.id,message:e.value})}const D3={"App.js":b3,"api.js":N3,"Userinfo.jsx":w3,"Button.jsx":y3,"Skeleton.jsx":S3};function L3(){return t.jsx(ae,{files:D3,renderArticle:n=>t.jsx(A3,{components:{code:n}}),caseRender:t.jsx(T3,{})})}const B3=`import { useState, Suspense } from 'react';
+`,t.jsx(e.p,{children:"希望大家能够通过这个案例,进一步感受到新的开发思维的强大之处。"})]})}function Yy(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Rm,{...n})}):Rm(n)}function Qy(){const[n,e]=f.useState(()=>[Dr()]);function s(){e([...n,Dr()])}return t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"text-right mb-4",children:t.jsx(fx,{onClick:s,children:"新增数据"})}),n.map((a,l)=>t.jsx(f.Suspense,{fallback:t.jsx(px,{}),children:t.jsx(Jy,{promise:a,index:l})},`hello ${l}`))]})}function Jy(n){const e=f.use(n.promise);return t.jsx(mx,{index:n.index,username:e.id,message:e.value})}const Py={"App.js":Dy,"api.js":By,"Userinfo.jsx":Uy,"Button.jsx":Oy,"Skeleton.jsx":zy};function Wy(){return t.jsx(W,{files:Py,renderArticle:n=>t.jsx(Yy,{components:{code:n}}),caseRender:t.jsx(Qy,{})})}const Ky=`import { useState, Suspense } from 'react';
import List from './List'
import Button from './Button'
import Skeleton from './Skeleton'
@@ -2084,7 +2084,7 @@ const Index = () => {
);
};
export default Index;
-`,O3=`import {twMerge} from 'tailwind-merge'
+`,Iy=`import {twMerge} from 'tailwind-merge'
import clsx from 'clsx'
document.documentElement.style.fontSize = '14px'
@@ -2124,14 +2124,14 @@ export default function Button(props) {
)
}
-`,z3=`const count = 3;
+`,Fy=`const count = 3;
const fakeDataUrl = \`https://randomuser.me/api/?results=\${count}&inc=name,gender,email,nat,picture&noinfo\`;
export const fetchList = async () => {
const res = await fetch(fakeDataUrl)
return res.json()
}
-`,U3=`// header
+`,e3=`// header
export default function Skeleton(props) {
return (
@@ -2150,7 +2150,7 @@ export default function Skeleton(props) {
)
}
-`,H3=`import { use } from 'react';
+`,n3=`import { use } from 'react';
export default function CurrentList({promise}) {
const {results} = use(promise)
return (
@@ -2238,7 +2238,7 @@ export default function CurrentList({promise}) {
`,t.jsx(e.p,{children:"完整的代码与演示效果请查看右侧案例。"}),`
`,t.jsxs(e.blockquote,{children:[`
`,t.jsx(e.p,{children:"分页参数的维护、最后一页的判断,大家在实践中要自行维护,这里只做方案的演示,没有考虑所有边界情况"}),`
-`]})]})}function q3(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Mm,{...n})}):Mm(n)}function V3({promise:n}){const{results:e}=h.use(n);return t.jsx("div",{children:e.map((s,a)=>t.jsxs("div",{className:"flex border-b py-4 mx-4 items-center border-dashed",children:[t.jsx("img",{className:"w-14 h-14 rounded-full",src:s.picture.large,alt:""}),t.jsxs("div",{className:"flex-1 ml-4",children:[t.jsx("div",{className:"font-bold",children:s.name.last}),t.jsx("div",{className:"text-gray-400 mt-1 text-sm line-clamp-1",children:"react 19 repo, a design language for background applications"})]})]},s.name.last))})}document.documentElement.style.fontSize="14px";function X3(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:c,success:i,...o}=n,p=Pe(se("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":i,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":c,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,c&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}function G3(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full mt-4",children:t.jsxs("div",{className:"animate-pulse flex space-x-4 items-center",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-3 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsx("div",{className:"space-y-3",children:t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]})})]})]})})}const Z3=3,Y3=`https://randomuser.me/api/?results=${Z3}&inc=name,gender,email,nat,picture&noinfo`,Am=async()=>(await fetch(Y3)).json(),Q3=()=>{const[n,e]=h.useState(()=>[Am()]),s=()=>{e([...n,Am()])};return t.jsxs(t.Fragment,{children:[n.map((a,l)=>t.jsx(h.Suspense,{fallback:t.jsx(G3,{}),children:t.jsx(V3,{promise:a})},`hello ${l}`)),t.jsx("div",{className:"text-center my-4",children:t.jsx(X3,{onClick:s,children:"loading more"})})]})},J3={"App.js":B3,"api.js":z3,"List.jsx":H3,"Button.jsx":O3,"Skeleton.jsx":U3};function K3(){return t.jsx(ae,{files:J3,renderArticle:n=>t.jsx(q3,{components:{code:n}}),caseRender:t.jsx(Q3,{})})}const W3=`import {useState, Suspense} from 'react'
+`]})]})}function t3(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Mm,{...n})}):Mm(n)}function s3({promise:n}){const{results:e}=f.use(n);return t.jsx("div",{children:e.map((s,a)=>t.jsxs("div",{className:"flex border-b py-4 mx-4 items-center border-dashed",children:[t.jsx("img",{className:"w-14 h-14 rounded-full",src:s.picture.large,alt:""}),t.jsxs("div",{className:"flex-1 ml-4",children:[t.jsx("div",{className:"font-bold",children:s.name.last}),t.jsx("div",{className:"text-gray-400 mt-1 text-sm line-clamp-1",children:"react 19 repo, a design language for background applications"})]})]},s.name.last))})}document.documentElement.style.fontSize="14px";function a3(n){const{className:e,primary:s,danger:a,sm:l,lg:r,signal:i,success:c,...o}=n,p=Ke(ae("rounded-md border border-transparent font-medium cursor-pointer transition relative","bg-gray-100 hover:bg-gray-200","text-xs py-2 px-4",{"bg-blue-500 text-white hover:bg-blue-600":s,"bg-red-500 text-white hover:bg-red-600":a,"bg-green-500 text-white hover:bg-green-600":c,"text-sky-500 bg-white border border-sky-300 hover:bg-sky-50":i,"text-xs py-1.5 px-3":l,"text-lg py-2 px-6":r},e));return t.jsxs("button",{className:p,...o,children:[n.children,i&&t.jsxs("span",{className:"absolute flex h-3 w-3 right-[-5px] top-[-5px]",children:[t.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"}),t.jsx("span",{className:"relative inline-flex rounded-full h-3 w-3 bg-sky-500"})]})]})}function l3(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full mt-4",children:t.jsxs("div",{className:"animate-pulse flex space-x-4 items-center",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-3 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsx("div",{className:"space-y-3",children:t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]})})]})]})})}const r3=3,i3=`https://randomuser.me/api/?results=${r3}&inc=name,gender,email,nat,picture&noinfo`,Em=async()=>(await fetch(i3)).json(),c3=()=>{const[n,e]=f.useState(()=>[Em()]),s=()=>{e([...n,Em()])};return t.jsxs(t.Fragment,{children:[n.map((a,l)=>t.jsx(f.Suspense,{fallback:t.jsx(l3,{}),children:t.jsx(s3,{promise:a})},`hello ${l}`)),t.jsx("div",{className:"text-center my-4",children:t.jsx(a3,{onClick:s,children:"loading more"})})]})},o3={"App.js":Ky,"api.js":Fy,"List.jsx":n3,"Button.jsx":Iy,"Skeleton.jsx":e3};function u3(){return t.jsx(W,{files:o3,renderArticle:n=>t.jsx(t3,{components:{code:n}}),caseRender:t.jsx(c3,{})})}const d3=`import {useState, Suspense} from 'react'
import Skeleton from './Skeleton'
import {fetchListWithCancel} from './api'
import List from './List'
@@ -2262,7 +2262,7 @@ export default function Demo01() {
)
}
-`,P3=`export const fetchList = async (number) => {
+`,m3=`export const fetchList = async (number) => {
const res = await fetch(\`https://randomuser.me/api/?results=\${number}&inc=name,gender,email,nat,picture&noinfo\`)
return res.json()
}
@@ -2288,7 +2288,7 @@ export const fetchListWithCancel = (number) => {
}
return promise
}
-`,F3=`document.documentElement.style.fontSize = '14px'
+`,p3=`document.documentElement.style.fontSize = '14px'
export default function Skeleton(props) {
return (
@@ -2309,7 +2309,7 @@ export default function Skeleton(props) {
)
}
-`,I3=`import { use } from 'react';
+`,f3=`import { use } from 'react';
export default function CurrentList({promise}) {
const {results} = use(promise)
console.log('我会执行几次呢')
@@ -2327,7 +2327,7 @@ export default function CurrentList({promise}) {
)
}
-`,ey=`export default function Input(props) {
+`,h3=`export default function Input(props) {
const {...other} = props
return (
@@ -2343,7 +2343,7 @@ export default function CurrentList({promise}) {
)
}
-`;document.documentElement.style.fontSize="14px";function ny(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full mt-4",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}const Tm=async n=>(await fetch(`https://randomuser.me/api/?results=${n}&inc=name,gender,email,nat,picture&noinfo`)).json();function ty({promise:n}){const{results:e}=h.use(n);return console.log("我会执行几次呢"),t.jsx("div",{children:e.map((s,a)=>t.jsxs("div",{className:"flex border p-4 items-center my-4 rounded-md",children:[t.jsx("img",{className:"w-12 h-12 rounded-full",src:s.picture.large,alt:""}),t.jsxs("div",{className:"flex-1 ml-4",children:[t.jsx("div",{className:"font-bold",children:s.name.last}),t.jsx("div",{className:"text-gray-400 mt-1 text-sm line-clamp-1",children:"react 19 re, a design language for background applications"})]})]},`h${a}`))})}function sy(n){const{...e}=n;return t.jsxs("div",{className:"flex items-center border px-2 rounded-md text-gray-500",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-5",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"})}),t.jsx("input",{className:"flex-1 !border-none p-2",style:{outline:"none"},type:"text",...e})]})}function ay(){const[n,e]=h.useState(()=>Tm(5));function s(a){const l=a.target.value.length%10;e(Tm(l))}return t.jsxs("div",{children:[t.jsx(sy,{onChange:s,placeholder:"Enter something"}),t.jsx(h.Suspense,{fallback:t.jsx(ny,{}),children:t.jsx(ty,{promise:n})})]})}function $m(n){const e={code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(T,{children:t.jsx(ay,{})}),`
+`;document.documentElement.style.fontSize="14px";function x3(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full mt-4",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}const Am=async n=>(await fetch(`https://randomuser.me/api/?results=${n}&inc=name,gender,email,nat,picture&noinfo`)).json();function g3({promise:n}){const{results:e}=f.use(n);return console.log("我会执行几次呢"),t.jsx("div",{children:e.map((s,a)=>t.jsxs("div",{className:"flex border p-4 items-center my-4 rounded-md",children:[t.jsx("img",{className:"w-12 h-12 rounded-full",src:s.picture.large,alt:""}),t.jsxs("div",{className:"flex-1 ml-4",children:[t.jsx("div",{className:"font-bold",children:s.name.last}),t.jsx("div",{className:"text-gray-400 mt-1 text-sm line-clamp-1",children:"react 19 re, a design language for background applications"})]})]},`h${a}`))})}function j3(n){const{...e}=n;return t.jsxs("div",{className:"flex items-center border px-2 rounded-md text-gray-500",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-5",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"})}),t.jsx("input",{className:"flex-1 !border-none p-2",style:{outline:"none"},type:"text",...e})]})}function v3(){const[n,e]=f.useState(()=>Am(5));function s(a){const l=a.target.value.length%10;e(Am(l))}return t.jsxs("div",{children:[t.jsx(j3,{onChange:s,placeholder:"Enter something"}),t.jsx(f.Suspense,{fallback:t.jsx(x3,{}),children:t.jsx(g3,{promise:n})})]})}function Tm(n){const e={code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(A,{children:t.jsx(v3,{})}),`
`,t.jsx(e.p,{children:"这是一个搜索的案例。当我们学会把数据存放在 promise 中时,实现这个案例的代码将会非常简单。案例演示如上面所示。"}),`
`,t.jsx(e.p,{children:"首先,我们需要定义一个 List 组件,用于显示列表数据。我们计划将列表数据存放在一个 promise 中,然后使用 use 从该 promise 中读取列表然后渲染。"}),`
`,t.jsx(e.p,{children:"因此,该 List 组件的代码如下所示"}),`
@@ -2468,7 +2468,7 @@ alert(signal.aborted); // true
update(fetchListWithCancel(len))
}
`})}),`
-`,t.jsx(e.p,{children:"其他的逻辑基本保持不变,完整代码和演示效果请在右侧区域查看。请务必结合调试工具中的网络请求一起观察演示效果。"})]})}function ly(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx($m,{...n})}):$m(n)}const ry={"App.js":W3,"api.js":P3,"List.jsx":I3,"Input.jsx":ey,"Skeleton.jsx":F3};function cy(){return t.jsx(ae,{files:ry,renderArticle:n=>t.jsx(ly,{components:{code:n}})})}const iy=`import {useState, Suspense} from 'react'
+`,t.jsx(e.p,{children:"其他的逻辑基本保持不变,完整代码和演示效果请在右侧区域查看。请务必结合调试工具中的网络请求一起观察演示效果。"})]})}function b3(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Tm,{...n})}):Tm(n)}const y3={"App.js":d3,"api.js":m3,"List.jsx":f3,"Input.jsx":h3,"Skeleton.jsx":p3};function N3(){return t.jsx(W,{files:y3,renderArticle:n=>t.jsx(b3,{components:{code:n}})})}const xx=`import {useState, Suspense} from 'react'
import Skeleton from './Skeleton'
import {fetchListWithCancel} from './api'
import Tabs from './Tabs'
@@ -2506,7 +2506,7 @@ export default function Example() {
)
}
-`,oy=`export const fetchList = async (number) => {
+`,gx=`export const fetchList = async (number) => {
const res = await fetch(\`https://randomuser.me/api/?results=\${number}&inc=name,gender,email,nat,picture&noinfo\`)
return res.json()
}
@@ -2532,7 +2532,7 @@ export const fetchListWithCancel = (number) => {
}
return promise
}
-`,uy=`document.documentElement.style.fontSize = '14px'
+`,jx=`document.documentElement.style.fontSize = '14px'
export default function Skeleton(props) {
return (
@@ -2553,7 +2553,7 @@ export default function Skeleton(props) {
)
}
-`,dy=`import { use } from 'react';
+`,vx=`import { use } from 'react';
export default function CurrentList({promise}) {
const {results} = use(promise)
console.log('我会执行几次呢')
@@ -2571,7 +2571,7 @@ export default function CurrentList({promise}) {
)
}
-`,my=`function classNames(...classes) {
+`,bx=`function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
@@ -2619,7 +2619,7 @@ export default function Tabs({tabs, onSwitch}) {
)
}
-`;function py(...n){return n.filter(Boolean).join(" ")}function ox({tabs:n,onSwitch:e}){return t.jsxs("div",{children:[t.jsxs("div",{className:"hidden",children:[t.jsx("label",{htmlFor:"tabs",className:"sr-only",children:"Select a tab"}),t.jsx("select",{id:"tabs",name:"tabs",className:"block w-full rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500",defaultValue:n.find(s=>s.current).name,onChange:s=>e(s.target.selectedIndex),children:n.map(s=>t.jsx("option",{children:s.name},s.name))})]}),t.jsx("div",{className:"block",children:t.jsx("nav",{className:"flex space-x-4","aria-label":"Tabs",children:n.map((s,a)=>t.jsx("a",{href:s.href,className:py(s.current?"bg-indigo-100 text-indigo-700":"text-gray-500 hover:text-gray-700","rounded-md px-3 py-2 text-sm font-medium"),"aria-current":s.current?"page":void 0,onClick:l=>{l.preventDefault(),e(a)},children:s.name},s.name))})})]})}const Vl=[{name:"My Account",href:"#",current:!0},{name:"Company",href:"#",current:!1},{name:"Team Members",href:"#",current:!1},{name:"Billing",href:"#",current:!1}];function fy(){const[n,e]=h.useState(0);function s(a){Vl[n].current=!1,Vl[a].current=!0,e(a)}return t.jsxs("div",{children:[t.jsx(ox,{tabs:Vl,onSwitch:s}),t.jsxs("div",{className:"mt-4 text-gray-500",children:["当前选中:",Vl[n].name]})]})}function Dm(n){const e={code:"code",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"Tab 切换的过程中,请求接口也是日常开发中非常常见的交互方式。不过 tabs 切换的情况比较多,很考验开发者的个人能力。我们准备了三个不同的场景以供大家直接学习。"}),`
+`;function S3(...n){return n.filter(Boolean).join(" ")}function yx({tabs:n,onSwitch:e}){return t.jsxs("div",{children:[t.jsxs("div",{className:"hidden",children:[t.jsx("label",{htmlFor:"tabs",className:"sr-only",children:"Select a tab"}),t.jsx("select",{id:"tabs",name:"tabs",className:"block w-full rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500",defaultValue:n.find(s=>s.current).name,onChange:s=>e(s.target.selectedIndex),children:n.map(s=>t.jsx("option",{children:s.name},s.name))})]}),t.jsx("div",{className:"block",children:t.jsx("nav",{className:"flex space-x-4","aria-label":"Tabs",children:n.map((s,a)=>t.jsx("a",{href:s.href,className:S3(s.current?"bg-indigo-100 text-indigo-700":"text-gray-500 hover:text-gray-700","rounded-md px-3 py-2 text-sm font-medium"),"aria-current":s.current?"page":void 0,onClick:l=>{l.preventDefault(),e(a)},children:s.name},s.name))})})]})}const Xl=[{name:"My Account",href:"#",current:!0},{name:"Company",href:"#",current:!1},{name:"Team Members",href:"#",current:!1},{name:"Billing",href:"#",current:!1}];function w3(){const[n,e]=f.useState(0);function s(a){Xl[n].current=!1,Xl[a].current=!0,e(a)}return t.jsxs("div",{children:[t.jsx(yx,{tabs:Xl,onSwitch:s}),t.jsxs("div",{className:"mt-4 text-gray-500",children:["当前选中:",Xl[n].name]})]})}function $m(n){const e={code:"code",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"Tab 切换的过程中,请求接口也是日常开发中非常常见的交互方式。不过 tabs 切换的情况比较多,很考验开发者的个人能力。我们准备了三个不同的场景以供大家直接学习。"}),`
`,t.jsxs(e.p,{children:["第一个案例比较简单。是",t.jsx(e.strong,{children:"多对一"}),":多个 tab 按钮,对应一个组件。具体表现为按钮切换时,该组件本身重新获取数据渲染。演示效果如右侧案例所示。"]}),`
`,t.jsxs(e.p,{children:["首先,我们需要先封装一个 Tab 切换按钮。具体的封装代码可以查看右面的 ",t.jsx(e.code,{children:"Tabs.jsx"})," 文件。"]}),`
`,t.jsx(e.p,{children:"然后使用一下,检测一下效果"}),`
@@ -2652,7 +2652,7 @@ export default function Example() {
)
}
`})}),`
-`,t.jsx(T,{children:t.jsx(fy,{})}),`
+`,t.jsx(A,{children:t.jsx(w3,{})}),`
`,t.jsx(e.p,{children:"这里需要注意观察的是,案例中我们对 tabs 数据和 current 当前选中项的一个管理方式。在使用过程中,我们可以尽量减少对于 state 的使用,能不用就不用。但是许多人在开发过程中会非常依赖于 state,管理不善时,可能会导致数据的大量冗余 re-render 产生。这里当我们切换点击时,会修改两个数据,但是最终只有一个 state 变化。"}),`
`,t.jsx(e.p,{children:"接下来的事情就比较简单了,跟之前的案例一样,只需要在切换时,通过改变 promise 的方式请求接口即可。"}),`
`,t.jsx(e.p,{children:"所以我们需要声明一个新的状态 promise"}),`
@@ -2669,7 +2669,7 @@ export default function Example() {
+ update(fetchListWithCancel(len))
}
`})}),`
-`,t.jsx(e.p,{children:"这里我们依然需要取消上一次请求是因为可能有的使用者会连续快速切换,我们可以通过取消为完成请求的方式让页面响应变得合理与流畅。完整的案例请看右侧区域。"})]})}function hy(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Dm,{...n})}):Dm(n)}document.documentElement.style.fontSize="14px";function xy(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full mt-4",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}const Lm=n=>{let e=new AbortController,s=e.signal;const a=new Promise(l=>{fetch(`https://randomuser.me/api/?results=${n}&inc=name,gender,email,nat,picture&noinfo`,{signal:s}).then(r=>{l(r.json())}).catch(()=>{console.log("接口成功取消!")})});return a.cancel=()=>{e&&e.abort()},a};function gy({promise:n}){const{results:e}=h.use(n);return console.log("我会执行几次呢"),t.jsx("div",{children:e.map((s,a)=>t.jsxs("div",{className:"flex border p-4 items-center my-4 rounded-md",children:[t.jsx("img",{className:"w-12 h-12 rounded-full",src:s.picture.large,alt:""}),t.jsxs("div",{className:"flex-1 ml-4",children:[t.jsx("div",{className:"font-bold",children:s.name.last}),t.jsx("div",{className:"text-gray-400 mt-1 text-sm line-clamp-1",children:"react 19 re, a design language for background applications"})]})]},`h${a}`))})}const Fc=[{name:"My Account",href:"#",current:!0},{name:"Company",href:"#",current:!1},{name:"Team Members",href:"#",current:!1},{name:"Billing",href:"#",current:!1}];function vy(){const[n,e]=h.useState(0),[s,a]=h.useState(()=>Lm(5));function l(r){Fc[n].current=!1,Fc[r].current=!0,e(r),s.cancel();const c=Math.floor(Math.random()*10);a(Lm(c))}return t.jsxs("div",{children:[t.jsx(ox,{tabs:Fc,onSwitch:l}),t.jsx(h.Suspense,{fallback:t.jsx(xy,{}),children:t.jsx(gy,{promise:s})})]})}const jy={"App.js":iy,"api.js":oy,"List.jsx":dy,"Tabs.jsx":my,"Skeleton.jsx":uy};function by(){return t.jsx(ae,{files:jy,renderArticle:n=>t.jsx(hy,{components:{code:n}}),caseRender:t.jsx(vy,{})})}const yy=`import {useState, Suspense, useRef} from 'react'
+`,t.jsx(e.p,{children:"这里我们依然需要取消上一次请求是因为可能有的使用者会连续快速切换,我们可以通过取消为完成请求的方式让页面响应变得合理与流畅。完整的案例请看右侧区域。"})]})}function _3(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx($m,{...n})}):$m(n)}document.documentElement.style.fontSize="14px";function k3(n){return t.jsx("div",{className:"border border-blue-100 shadow rounded-md p-4 w-full mt-4",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}const Lm=n=>{let e=new AbortController,s=e.signal;const a=new Promise(l=>{fetch(`https://randomuser.me/api/?results=${n}&inc=name,gender,email,nat,picture&noinfo`,{signal:s}).then(r=>{l(r.json())}).catch(()=>{console.log("接口成功取消!")})});return a.cancel=()=>{e&&e.abort()},a};function C3({promise:n}){const{results:e}=f.use(n);return console.log("我会执行几次呢"),t.jsx("div",{children:e.map((s,a)=>t.jsxs("div",{className:"flex border p-4 items-center my-4 rounded-md",children:[t.jsx("img",{className:"w-12 h-12 rounded-full",src:s.picture.large,alt:""}),t.jsxs("div",{className:"flex-1 ml-4",children:[t.jsx("div",{className:"font-bold",children:s.name.last}),t.jsx("div",{className:"text-gray-400 mt-1 text-sm line-clamp-1",children:"react 19 re, a design language for background applications"})]})]},`h${a}`))})}const Ii=[{name:"My Account",href:"#",current:!0},{name:"Company",href:"#",current:!1},{name:"Team Members",href:"#",current:!1},{name:"Billing",href:"#",current:!1}];function Nx(){const[n,e]=f.useState(0),[s,a]=f.useState(()=>Lm(5));function l(r){Ii[n].current=!1,Ii[r].current=!0,e(r),s.cancel();const i=Math.floor(Math.random()*10);a(Lm(i))}return t.jsxs("div",{children:[t.jsx(yx,{tabs:Ii,onSwitch:l}),t.jsx(f.Suspense,{fallback:t.jsx(k3,{}),children:t.jsx(C3,{promise:s})})]})}const R3={"App.js":xx,"api.js":gx,"List.jsx":vx,"Tabs.jsx":bx,"Skeleton.jsx":jx};function M3(){return t.jsx(W,{files:R3,renderArticle:n=>t.jsx(_3,{components:{code:n}}),caseRender:t.jsx(Nx,{})})}const E3=`import {useState, Suspense, useRef} from 'react'
import Tabs from './Tabs'
import Account from './Account'
@@ -2710,7 +2710,7 @@ export default function Example() {
)
}
-`,Ny=`export const fetchListWithCancel = (number) => {
+`,A3=`export const fetchListWithCancel = (number) => {
let controller = new AbortController();
let signal = controller.signal;
const promise = new Promise(resolve => {
@@ -2731,7 +2731,7 @@ export default function Example() {
}
return promise
}
-`,Sy=`document.documentElement.style.fontSize = '14px'
+`,T3=`document.documentElement.style.fontSize = '14px'
export default function Skeleton(props) {
return (
@@ -2752,7 +2752,7 @@ export default function Skeleton(props) {
)
}
-`,wy=`import { use } from 'react';
+`,$3=`import { use } from 'react';
export default function CurrentList({promise}) {
const {results} = use(promise)
@@ -2770,7 +2770,7 @@ export default function CurrentList({promise}) {
)
}
-`,_y=`export default function Input(props) {
+`,L3=`export default function Input(props) {
const {...other} = props
return (
{cart.map((item, index) => (
@@ -8036,10 +8035,10 @@ export default BookItem;
}
export default CartList
-`;function hp(n){const e={blockquote:"blockquote",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsxs(e.blockquote,{children:[`
+`;function fp(n){const e={blockquote:"blockquote",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsxs(e.blockquote,{children:[`
`,t.jsx(e.p,{children:"注:由于 FormAction 在 next.js 中的表现暂时还不稳定,因此,本文以及后续文章的探讨全部都是基于他们在客户端的交互与表现"}),`
`]}),`
-`,t.jsx(T,{children:t.jsx(cN,{})}),`
+`,t.jsx(A,{children:t.jsx(x8,{})}),`
`,t.jsx(e.h2,{children:"1"}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"useActionState"})}),`
`,t.jsxs(e.p,{children:[t.jsx(e.code,{children:"useActionState"})," 与 useState 的使用基本上是一致的。它同样可以用来定义一个",t.jsx(e.strong,{children:"状态"}),"。他和 useState 不一样的是,",t.jsx(e.code,{children:"useActionState"})," 需要结合 form action 使用,它的更新机制依赖于 action。实例代码如下所示。"]}),`
@@ -8066,8 +8065,8 @@ export default function Demo01() {
`,t.jsx(e.h2,{children:"2"}),`
`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"复杂案例"})}),`
`,t.jsx(e.p,{children:"大家先来看一下演示效果。然后再根据代码学习,这是一个比较复杂的案例。"}),`
-`,t.jsx(T,{reload:!0,children:t.jsx(dN,{})}),`
-`,t.jsx(In,{files:{Demo02:pN,BookItem:hN,"actions.js":mN,SubmitButton:fN,List:xN}}),`
+`,t.jsx(A,{reload:!0,children:t.jsx(b8,{})}),`
+`,t.jsx(Dn,{files:{Demo02:N8,BookItem:w8,"actions.js":y8,SubmitButton:S8,List:_8}}),`
`,t.jsxs(e.blockquote,{children:[`
`,t.jsxs(e.p,{children:["在 next.js 中使用时,我们可以把 action 单独执行服务端的逻辑,从而轻松做到客户端组件与服务端组件的交互。上面案例中的 ",t.jsx(e.code,{children:"actions.js"})," 则表示服务端的代码"]}),`
`]}),`
@@ -8087,7 +8086,705 @@ export async function addToCart(prevState, queryData) {
};
}
}
-`})})]})}function gN(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(hp,{...n})}):hp(n)}function vN(){return t.jsx(Ot,{renderArticle:n=>t.jsx(gN,{components:{code:n}})})}const Sx=[{type:"tip",name:"前言"},{path:"index",name:"1、开发方式变革",component:O5},{path:"layers",name:"2、学习的三个层次",component:W5},{path:"create",name:"3、创建项目",component:a4},{type:"tip",name:"use(promise)"},{path:"use/base",name:"3、use 基础知识",component:N4},{path:"use/suspense",name:"4、Suspense",component:H4},{path:"use/update",name:"5、点击更新数据",component:s3},{path:"use/initialize",name:"6、初始化请求并更新",component:j3},{path:"use/updatetolist",name:"7、请求并新增到列表",component:L3},{path:"use/loadmore",name:"8、分页列表加载更多",component:K3},{path:"use/search",name:"9、搜索",component:cy},{path:"use/tabs",name:"10、tab 简单切换",component:by,label:"1"},{path:"use/tabshard",name:"11、tab 缓存切换",component:By,label:"2"},{path:"use/fromchildren",name:"12、父级获取数据",component:Ky,label:"3"},{path:"use/nest",name:"13、Suspense 嵌套",component:r6},{path:"use/react16",name:"14、在低版本中使用",component:g6},{type:"tip",name:"use(context)"},{path:"use/ref",name:"15、ref 调整",component:$6},{path:"use/modal",name:"16、自定义弹窗组件",component:H6},{path:"use/contextmodify",name:"17、弹窗中更改内容",component:P6},{path:"use/skinswitch",name:"18、皮肤切换次数",component:i7},{type:"tip",name:"并发 API"},{path:"use/deferred",name:"19、useDeferredValue",component:_7},{path:"use/transition",name:"20、useTransition",component:G7},{type:"tip",name:"Compiler"},{path:"use/importwith19",name:"21、React 19 中引入",component:J7},{path:"use/importwithlower",name:"22、低版本中引入",component:I7},{path:"use/importwithwebpack",name:"23、webpack中引入",component:a8},{path:"use/compilerbase",name:"24、Compiler 编译原理",component:u8},{path:"use/compilercount",name:"25、收益分析-递增",component:x8},{path:"use/compilerexpensive",name:"26、收益分析-耗时子组件",component:S8},{path:"use/compilertabs",name:"27、收益分析-tabs",component:U8},{type:"tip",name:"form action"},{path:"formaction/base",name:"28、form 基础",component:G8},{path:"formaction/formdata",name:"29、FormData 基础",component:Y8},{path:"formaction/action",name:"30、form action",component:I8},{path:"formaction/useformstatus",name:"31、useFormStatus",component:lN},{path:"formaction/useactionstate",name:"32、useActionState",component:vN}];function xp(){return t.jsx("div",{children:Sx.map((n,e)=>n.type==="tip"?t.jsx("div",{className:"mx-2 text-sm px-4 text-gray-400 first:pt-0 pb-4 pt-8",children:n.name},`z-${e}`):t.jsx(Cn,{className:"text-gray-700",activeName:"text-blue-700",to:n.path,children:t.jsxs("li",{className:"mx-2 px-4 py-3 transition hover:bg-blue-100 text-sm flex items-center justify-between hover:text-blue-700 rounded",children:[t.jsxs("div",{className:"flex items-center",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-4",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"})}),t.jsx("div",{className:"ml-2 line-clamp-1",children:n.name})]}),n.label?t.jsx(Q0,{label:n.label,primary:!0,className:"ml-2"}):null]},n.path)},n.path))})}function jN(){const n=ru(),e=h.useRef(new Map),s=cu(),{pathname:a}=cs();h.useReducer(r=>!r)[1];const l=h.useRef(null);return e.current.has(a)||e.current.set(a,s),h.useEffect(()=>{location.pathname==="/tutorial"&&n("index")},[a]),t.jsxs("div",{className:"pt-16 md:flex",children:[t.jsx(le,{signal:!0,className:"fixed bottom-6 right-6 z-40 md:hidden",onClick:()=>l.current.show(),children:"目录"}),t.jsx(nc,{ref:l,onClose:()=>l.current.close(),children:t.jsx("div",{className:"bg-white h-full py-4 overflow-scroll",children:t.jsx(xp,{})})}),t.jsxs("nav",{className:"hidden md:block w-60 sticky top-16 h-[calc(100vh_-_5rem)] overflow-y-auto bg-hei",children:[t.jsxs("div",{className:"flex items-center px-4 py-8",children:[t.jsx("div",{className:"flex items-center justify-between p-1 border mr-3 rounded-md border-cyan-200",children:t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",className:"size-5 text-cyan-500",children:t.jsx("path",{fillRule:"evenodd",d:"M19.5 21a3 3 0 0 0 3-3V9a3 3 0 0 0-3-3h-5.379a.75.75 0 0 1-.53-.22L11.47 3.66A2.25 2.25 0 0 0 9.879 3H4.5a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h15Zm-6.75-10.5a.75.75 0 0 0-1.5 0v4.19l-1.72-1.72a.75.75 0 0 0-1.06 1.06l3 3a.75.75 0 0 0 1.06 0l3-3a.75.75 0 1 0-1.06-1.06l-1.72 1.72V10.5Z",clipRule:"evenodd"})})}),t.jsxs("div",{className:"text-sm",children:[t.jsx("div",{children:"当前版本"}),t.jsx("div",{className:"text-gray-500",children:"React@19.0.0-rc"})]})]}),t.jsx("ul",{className:"border-r h-[calc(100vh_-_9rem_-_35px)] overflow-y-auto pb-8",children:t.jsx(xp,{})})]}),t.jsx("div",{className:"md:flex-1 md:w-[calc(100vw_-_15rem)] p-4 md:p-8 min-h-[calc(100vh_-_5rem)] box-border",children:Array.from(e.current).map(([r,c])=>t.jsx("div",{style:{display:a===r?"block":"none"},children:c},r))})]})}function gp(n){const e={code:"code",p:"p",pre:"pre",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"基础使用"}),`
+`})})]})}function k8(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(fp,{...n})}):fp(n)}function C8(){return t.jsx(rt,{renderArticle:n=>t.jsx(k8,{components:{code:n}})})}const R8=`import { useOptimistic, useState, useRef } from "react";
+import { getMessage } from "./api";
+import {reducer} from './reducer'
+
+export default function Index() {
+ const [messages, setMessages] = useState([]);
+ const [optimisticMessages, addOptimisticMessage] = useOptimistic(messages, reducer);
+
+ const form = useRef(null);
+
+ async function formAction(formData) {
+ let newMessage = formData.get("message")
+ addOptimisticMessage(newMessage);
+ form.current.reset();
+ let message = await getMessage(newMessage);
+ setMessages([...messages, {text: message}])
+ }
+
+ return (
+ <>
+
+
+ {optimisticMessages.map((message, index) => (
+
+ {message.text} {!!message.sending && (Sending...)}
+
+ ))}
+ >
+ );
+}
+
+`,M8=`const random = [
+ 'React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.',
+ 'Whether you work on your own or with thousands of other developers, using React feels the same.',
+ 'React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.',
+ 'This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ',
+ 'You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it.'
+]
+
+var myHeaders = new Headers();
+myHeaders.append("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
+
+var requestOptions = {
+ method: 'GET',
+ headers: myHeaders,
+ redirect: 'follow',
+ data: 'hex'
+};
+
+// 你可以动态修改最后的数字,然后观察请求情况
+const url = 'https://echo.apifox.com/delay/1'
+
+export const getMessage = async (message) => {
+ await fetch(url, requestOptions)
+ return message
+}
+`,E8=`export function reducer(state, newMessage) {
+ let newItem = {
+ text: newMessage,
+ sending: true
+ }
+ return [...state, newItem]
+}`;var Ox=new Headers;Ox.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var A8={method:"GET",headers:Ox,redirect:"follow",data:"hex"};const T8="https://echo.apifox.com/delay/1",$8=async n=>(await fetch(T8,A8),n);function L8(n,e){let s={text:e,sending:!0};return[...n,s]}function so(){const[n,e]=f.useState([]),[s,a]=f.useOptimistic(n,L8),l=f.useRef(null);async function r(i){let c=i.get("message");a(c),l.current.reset();let o=await $8(c);e([...n,{text:o}])}return t.jsxs(t.Fragment,{children:[t.jsxs("form",{action:r,ref:l,className:"flex",children:[t.jsx("input",{type:"text",name:"message",placeholder:"enter your message"}),t.jsx("button",{type:"submit",className:"ml-2",children:"Send"})]}),s.map((i,c)=>t.jsxs("div",{className:"indent-1 text-slate-600 mt-1 hover:bg-slate-100 p-2 cursor-pointer rounded",children:[i.text," ",!!i.sending&&t.jsx("small",{children:" (Sending...)"})]},c))]})}function hp(n){const e={blockquote:"blockquote",code:"code",h3:"h3",hr:"hr",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.h3,{children:t.jsx(e.strong,{children:"useOptimistic"})}),`
+`,t.jsxs(e.p,{children:["如果你在之前的项目开发过程中,被乐观更新的需求折磨过,那么你一定会喜欢这个新 hook:",t.jsx(e.code,{children:"useOptimistic"}),"。它让原本实现起来比较困难的乐观更新,变得非常简单。"]}),`
+`,t.jsx(e.p,{children:"通常是指在提交数据时,乐观估计请求结果,不等待真实的请求结果,而直接基于乐观结果修改页面状态的交互方式。例如,我们在聊天软件中,发送一条消息时,当我们点击发送之后,消息就会立即出现在聊天界面。而不会等待发送成功之后再更新页面 UI"}),`
+`,t.jsx(e.p,{children:"普通的逻辑为"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-bash",children:`1、事件触发
+2、发起请求,等待请求成功 Loading
+3、请求成功
+4、更新 UI 为正确结果
+`})}),`
+`,t.jsx(e.p,{children:"乐观更新的执行过程为"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-bash",children:`1、事件触发 -> 直接更新 UI
+2、更新 UI 的同时发起请求 Loading
+3、请求成功 -> 保持UI 不变
+4、若请求失败 -> 则回退 UI
+`})}),`
+`,t.jsx(e.p,{children:"乐观更新在合适的场景之下,能够更加快速的响应用户交互,在体验上更好一些,因此这是许多项目都想要追求的交互结果。"}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"适用场景"}),`
+`,t.jsx(e.p,{children:"并不是所有的操作都适合乐观更新,这需要一些明确的前提条件"}),`
+`,t.jsxs(e.ul,{children:[`
+`,t.jsx(e.li,{children:"1、请求成功的概率非常大,几乎不会失败"}),`
+`,t.jsx(e.li,{children:"2、不涉及到频繁的,密集的 UI 变化"}),`
+`,t.jsx(e.li,{children:"3、可撤回的 UI 变化"}),`
+`,t.jsx(e.li,{children:"4、与服务端的反馈时间短,不是一个长期的持续的响应过程"}),`
+`]}),`
+`,t.jsx(e.p,{children:"例如,在聊天软件中,发送一条消息,在阅读文章时,点赞收藏按钮的交互,给文章发送一条评论,删除一条评论等都非常适合乐观更新。"}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"实现乐观更新需要具备的技术条件"}),`
+`,t.jsxs(e.p,{children:["由于乐观更新是一种在",t.jsx(e.strong,{children:"低概率"}),"的情况下,需要撤回更新状态的交互机制,因此,我们在第一时间更新到最新状态时,需要保留上一次的更新状态以便撤回。"]}),`
+`,t.jsxs(e.p,{children:["这样的场景与 ",t.jsx(e.code,{children:"redux/useReducer"})," 需要的技术架构非常类似。因此,每一次的更新我们都可以将其设计为一次 action,通过 reducer 的方式将其合并到完整数据中去"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`interface Action {
+ // 操作方式
+ type: string,
+ // 乐观更新的数据结构
+ state: {
+ id: 'xxx',
+ text: 'xxx'
+ }
+}
+`})}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`// 假设 state 是一个列表
+reducer(state, action) {
+ return [...state, action.state]
+}
+`})}),`
+`,t.jsx(e.p,{children:"如果保留了上一次的更新状态,我们也可以非常方便的还原数据。"}),`
+`,t.jsxs(e.p,{children:["除此之外,乐观更新的数据结构是我们在客户端根据预估情况生成的,因此不能直接存储在服务端,有的数据需要按照服务端的逻辑来创建,例如一条数据包含了 ",t.jsx(e.code,{children:"id"}),",那么我们就不能按照客户端的逻辑来创建 id,这个时候,需要我们",t.jsx(e.strong,{children:"在接口请求成功之后,完整的完成一次数据的替换"}),"。"]}),`
+`,t.jsx(e.p,{children:"最后,还有一个非常重要的问题。那就是更新快速重复的发生时如何处理。这是乐观更新最考验开发者技术能力的地方。"}),`
+`,t.jsx(e.p,{children:"当第一次请求还没结束的时候,但是此时当乐观更新重复发生,就会引发一系列不合理的问题。因此,什么时候将 action 合并到真实数据中去,就需要反正斟酌。"}),`
+`,t.jsx(e.p,{children:"这里不仅要考虑更新失败时我们应该如何处理,更需要考虑竞态的顺序问题,我们必须以 action 创建的顺序将 action 合并到数据中。"}),`
+`,t.jsx(e.p,{children:"在保证顺序的这个基础之上,我们还需要考虑前面如果某个 action 迟迟得不到响应,会阻塞后面 action 的合并。因此,我们还需要设计一个合理的超时机制。"}),`
+`,t.jsxs(e.blockquote,{children:[`
+`,t.jsx(e.p,{children:"所以,如果我们自己来设计一套完善的乐观更新机制,对开发者开发能力的要求非常高,我们可以将其作为项目亮点在面试中去介绍"}),`
+`]}),`
+`,t.jsx(e.p,{children:"因此,显而易见的是,基于并发模式的 React,解决乐观更新这类交互问题非常的适合,接下来我们就一起来学习一下 React 19 中,针对乐观更新提出来的解决方案"}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"React 19 是如何实现乐观更新的"}),`
+`,t.jsx(e.p,{children:"React 19 针对乐观更新,提出了一个新的 hook,useOptimistic"}),`
+`,t.jsxs(e.blockquote,{children:[`
+`,t.jsx(e.p,{children:"注意,乐观更新完整的技术实现一定要结合我们刚才所提到的技术基础来理解,单独只学习一个 hook,无法构成乐观更新的完整方案"}),`
+`]}),`
+`,t.jsx(e.p,{children:"它的基础语法如下"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
+`})}),`
+`,t.jsxs(e.p,{children:["注意看,useOptimistic 接收两个参数,其实这两个参数与 ",t.jsx(e.code,{children:"reducer"})," 的参数非常相似。"]}),`
+`,t.jsxs(e.p,{children:[t.jsx(e.code,{children:"state"})," 表示当前状态,",t.jsx(e.code,{children:"updateFn"})," 表示我们如何将新的 action 合并到 ",t.jsx(e.code,{children:"state"})," 中去"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`updateFn = (currentState, value) => {
+ // 根据上一次状态与新的 value 合并
+ // merge and return new state
+}
+`})}),`
+`,t.jsxs(e.p,{children:[t.jsx(e.strong,{children:"optimisticState"})," 表示合并之后的新状态。但是这里我们需要特别注意的是,它是一个临时状态,并非最终状态。通常情况下,我们会使用该临时状态渲染 UI,以便 UI 能够得到最快速的响应。"]}),`
+`,t.jsxs(e.p,{children:[t.jsx(e.strong,{children:"addOptimistic"})," 是一次操作行为,类似于 dispatch,它会将参数传递给 ",t.jsx(e.code,{children:"updateFn"})]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`addOptimistic({a: 1})
+
+->
+
+// 此时 value = {a: 1}
+updateFn = (currentState, value) => {
+ return [...currentState, value]
+}
+`})}),`
+`,t.jsx(e.p,{children:"因此,在使用 useOptimistic 之前,我们还需要借助 useState 创造一个状态,该状态为更新的真实状态。我们通过 useOptimistic 得到的状态是一个副本,它通过 useState 的状态来初始化,在接口请求成功之后,真实状态才会得到更新。"}),`
+`,t.jsx(e.p,{children:"接下来,我们来实现一个简单的案例。"}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"案例一:消息发送"}),`
+`,t.jsx(A,{children:t.jsx(so,{})}),`
+`,t.jsx(e.p,{children:"我们要实现的效果案例如上。"}),`
+`,t.jsxs(e.p,{children:["首先明确一点,消息发送是一个异步过程,因此我们把这个过程使用 ",t.jsx(e.code,{children:"Sending..."})," 字符来表示,当每条消息的 ",t.jsx(e.code,{children:"Sending..."})," 消失,才表示数据更新成功。"]}),`
+`,t.jsx(e.p,{children:"先来考虑布局。"}),`
+`,t.jsx(e.p,{children:"首先我们需要一个 form 表单来处理输入的交互"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-html",children:`
+`})}),`
+`,t.jsxs(e.p,{children:["然后我们需要一个列表来渲染输出之后的结果。根据我们之前的学习结果,该列表需要用 ",t.jsx(e.code,{children:"useOptimistic"})," 返回的临时状态来处理,这样我们才能够第一时间在 UI 上看到反馈结果"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-jsx",children:`{optimisticMessages.map((message, index) => (
+
+ {message.text}
+ {!!message.sending && (Sending...)}
+
+))}
+`})}),`
+`,t.jsx(e.p,{children:"再来思考状态如何设计。"}),`
+`,t.jsx(e.p,{children:"首先我们需要使用 useState 来设计一个状态,用于存储真实的状态结果"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`const [messages, setMessages] = useState([]);
+`})}),`
+`,t.jsxs(e.p,{children:["然后我们需要使用 ",t.jsx(e.code,{children:"useOptimistic"})," 来设计临时状态,这里需要注意的是,我们可以把它当成一个 reducer 来看待,第一个参数表示当前状态,第二参数表示一个合并方式"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`const [optimisticMessages, addOptimisticMessage] = useOptimistic(
+ messages,
+ (state, newMessage) => [
+ ...state,
+ {
+ text: newMessage,
+ sending: true
+ }
+ ]
+);
+`})}),`
+`,t.jsxs(e.p,{children:["临时状态中包含一个 ",t.jsx(e.code,{children:"sending: true"}),",用于标识当前请求正在发生。"]}),`
+`,t.jsxs(e.p,{children:["在 ",t.jsx(e.code,{children:"formAction"})," 回调函数中,我们会调用 ",t.jsx(e.code,{children:"addOptimisticMessage"})," 立即更新临时状态,并发送请求,我们提前把发送请求的接口写好"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`// actions.js
+export async function deliverMessage(message) {
+ await new Promise((res) => setTimeout(res, 1000));
+ return message;
+}
+`})}),`
+`,t.jsxs(e.p,{children:["那么,",t.jsx(e.code,{children:"formAction"})," 的完整逻辑为"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`async function formAction(formData) {
+ let newMessage = formData.get("message")
+ addOptimisticMessage(newMessage);
+ let message = await deliverMessage(newMessage);
+ setMessages([...messages, {text: message}])
+}
+`})}),`
+`,t.jsxs(e.blockquote,{children:[`
+`,t.jsx(e.p,{children:"请求发送成功之后,更新真实状态"}),`
+`]}),`
+`,t.jsx(e.p,{children:"这样,一个简单的乐观更新交互,我们就完成了,该案例的完整代码右侧所示。"}),`
+`,t.jsx(A,{children:t.jsx(so,{})}),`
+`,t.jsxs(e.blockquote,{children:[`
+`,t.jsx(e.p,{children:"reset() 用于立即重置表单内容,可进行下一次输入。默认行为是接口请求成功之后才会重置"}),`
+`]}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"问题"}),`
+`,t.jsxs(e.p,{children:["尝试一下快速连续输入内容并 ",t.jsx(e.code,{children:"Send"}),",我们会发现,最终的效果并非我们所愿,此时会有多条内容同时正在 ",t.jsx(e.code,{children:"Sending"}),",但是最终请求成功之后,这些同时的 ",t.jsx(e.code,{children:"Sending"})," 内容仅有一条信息被合并到最终结果中,其他的内容会消失。那么在此基础之上,我们需要如何来解决这个问题呢?下一章我们继续学习。"]})]})}function D8(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(hp,{...n})}):hp(n)}const O8={"App.js":R8,"api.js":M8,"reducer.js":E8};function B8(){return t.jsx(W,{files:O8,renderArticle:n=>t.jsx(D8,{components:{code:n}}),caseRender:t.jsx(so,{})})}const z8=`import { useOptimistic, useState, useRef, useTransition } from "react";
+import { getMessage } from "./api";
+import {reducer} from './reducer'
+
+export default function Index() {
+ const [messages, setMessages] = useState([]);
+ const [optimisticMessages, addOptimisticMessage] = useOptimistic(messages, reducer);
+ const [isPending, startTransition] = useTransition()
+
+ const form = useRef(null);
+
+ async function formAction(formData) {
+ let newMessage = formData.get("message")
+ form.current.reset();
+ startTransition(async () => {
+ addOptimisticMessage(newMessage);
+ let message = await getMessage(newMessage);
+ setMessages([...messages, {text: message}])
+ })
+ }
+
+ return (
+ <>
+
+
+ {optimisticMessages.map((message, index) => (
+
+ {message.text} {!!message.sending && (Sending...)}
+
+ ))}
+ >
+ );
+}
+
+`,U8=`const random = [
+ 'React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.',
+ 'Whether you work on your own or with thousands of other developers, using React feels the same.',
+ 'React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.',
+ 'This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ',
+ 'You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it.'
+]
+
+var myHeaders = new Headers();
+myHeaders.append("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
+
+var requestOptions = {
+ method: 'GET',
+ headers: myHeaders,
+ redirect: 'follow',
+ data: 'hex'
+};
+
+// 你可以动态修改最后的数字,然后观察请求情况
+const url = 'https://echo.apifox.com/delay/1'
+
+export const getMessage = async (message) => {
+ await fetch(url, requestOptions)
+ return message
+}
+`,H8=`export function reducer(state, newMessage) {
+ let newItem = {
+ text: newMessage,
+ sending: true
+ }
+ return [...state, newItem]
+}`;var Bx=new Headers;Bx.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var q8={method:"GET",headers:Bx,redirect:"follow",data:"hex"};const V8="https://echo.apifox.com/delay/1",X8=async n=>(await fetch(V8,q8),n);function G8(n,e){let s={text:e,sending:!0};return[...n,s]}function zx(){const[n,e]=f.useState([]),[s,a]=f.useOptimistic(n,G8),[l,r]=f.useTransition(),i=f.useRef(null);async function c(o){let u=o.get("message");i.current.reset(),r(async()=>{a(u);let d=await X8(u);e([...n,{text:d}])})}return t.jsxs(t.Fragment,{children:[t.jsxs("form",{action:c,ref:i,className:"flex",children:[t.jsx("input",{type:"text",name:"message",placeholder:"enter your message",disabled:l}),t.jsx("button",{type:"submit",className:"ml-2",disabled:l,children:"Send"})]}),s.map((o,u)=>t.jsxs("div",{className:"indent-1 text-slate-600 mt-1 hover:bg-slate-100 p-2 cursor-pointer rounded",children:[o.text," ",!!o.sending&&t.jsx("small",{children:" (Sending...)"})]},u))]})}function xp(n){const e={code:"code",h3:"h3",hr:"hr",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.h3,{children:"案例二:结合 useTransition"}),`
+`,t.jsx(e.p,{children:"在上一章的案例中,当我们快速发送多条信息时,我们发现,并不是每一条消息都被成功合并到真实状态中了。最终结果是有的消息不见了。那如何解决这个问题呢?"}),`
+`,t.jsxs(e.p,{children:["我们可以结合 ",t.jsx(e.code,{children:"useTransition"})," 来防止用户连续触发 ",t.jsx(e.code,{children:"formAction"})," 的执行"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`const [isPending, startTransition] = useTransition()
+`})}),`
+`,t.jsx(e.p,{children:"formAction 的定义调整为:"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`async function formAction(formData) {
+ let newMessage = formData.get("message")
+ form.current.reset()
+ startTransition(async () => {
+ addOptimisticMessage(newMessage);
+ let message = await deliverMessage(newMessage);
+ setMessages((messages) => [...messages, {text: message}])
+ })
+}
+`})}),`
+`,t.jsxs(e.p,{children:["然后使用 ",t.jsx(e.code,{children:"isPending"})," 来控制输入的禁用状态"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-html",children:`
+`})}),`
+`,t.jsx(e.p,{children:"感受一下最终演示效果"}),`
+`,t.jsx(A,{children:t.jsx(zx,{})}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"问题"}),`
+`,t.jsxs(e.p,{children:["很明显,这并不是最合理的交互方案。我们期望的是,",t.jsx(e.strong,{children:"连续输入依然能够发生"}),",在这个基础之上我们可以控制好数据的合并逻辑,那么借助 react 19 的机制,我们可以如何实现呢?"]})]})}function Z8(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(xp,{...n})}):xp(n)}const Y8={"App.js":z8,"api.js":U8,"reducer.js":H8};function Q8(){return t.jsx(W,{files:Y8,renderArticle:n=>t.jsx(Z8,{components:{code:n}}),caseRender:t.jsx(zx,{})})}const J8=`import { useState, useRef, use, Suspense } from "react";
+import { getMessage } from "./api";
+
+export default function Index() {
+ const [actions, updateActions] = useState([]);
+ const form = useRef(null);
+
+ function formAction(formData) {
+ let newMessage = formData.get("message")
+ form.current.reset();
+ let action = {
+ newMessage,
+ promise: getMessage(newMessage)
+ }
+ updateActions((actions) => [...actions, action])
+ }
+
+ return (
+ <>
+
+
+ {actions.map((action, index) => (
+
{action.newMessage}(Seding...)}>
+
+
+ ))}
+ >
+ );
+}
+
+function Message({children}) {
+ return (
+
+ {children}
+
+ )
+}
+
+function ListItem(props) {
+ const msg = use(props.promise)
+ return (
+
{msg}
+ )
+}`,P8=`const random = [
+ 'React lets you build user interfaces out of individual pieces called components. Create your own React components like Thumbnail, LikeButton, and Video. Then combine them into entire screens, pages, and apps.',
+ 'Whether you work on your own or with thousands of other developers, using React feels the same.',
+ 'React components are JavaScript functions. Want to show some content conditionally? Use an if statement. Displaying a list? Try array map(). Learning React is learning programming.',
+ 'This markup syntax is called JSX. It is a JavaScript syntax extension popularized by React. ',
+ 'You don’t have to build your whole page in React. Add React to your existing HTML page, and render interactive React components anywhere on it.'
+]
+
+var myHeaders = new Headers();
+myHeaders.append("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
+
+var requestOptions = {
+ method: 'GET',
+ headers: myHeaders,
+ redirect: 'follow',
+ data: 'hex'
+};
+
+// 你可以动态修改最后的数字,然后观察请求情况
+const url = 'https://echo.apifox.com/delay/1'
+
+export const getMessage = async (message) => {
+ await fetch(url, requestOptions)
+ return message
+}
+`;var Ux=new Headers;Ux.append("User-Agent","Apifox/1.0.0 (https://apifox.com)");var W8={method:"GET",headers:Ux,redirect:"follow",data:"hex"};const K8="https://echo.apifox.com/delay/1",I8=async n=>(await fetch(K8,W8),n);function Hx(){const[n,e]=f.useState([]),s=f.useRef(null);function a(l){let r=l.get("message");s.current.reset();let i={newMessage:r,promise:I8(r)};e(c=>[...c,i])}return t.jsxs(t.Fragment,{children:[t.jsxs("form",{action:a,ref:s,className:"flex",children:[t.jsx("input",{type:"text",name:"message",placeholder:"enter your message"}),t.jsx("button",{type:"submit",className:"ml-2",children:"Send"})]}),n.map((l,r)=>t.jsx(f.Suspense,{fallback:t.jsxs(qx,{children:[l.newMessage,"(Seding...)"]}),children:t.jsx(F8,{promise:l.promise})},`h-${r}`))]})}function qx({children:n}){return t.jsx("div",{className:"indent-1 text-slate-600 mt-1 hover:bg-slate-100 p-2 cursor-pointer rounded",children:n})}function F8(n){const e=f.use(n.promise);return t.jsx(qx,{children:e})}function gp(n){const e={blockquote:"blockquote",code:"code",h3:"h3",p:"p",pre:"pre",strong:"strong",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.h3,{children:t.jsx(e.strong,{children:"解决上一章的问题"})}),`
+`,t.jsx(e.p,{children:"我们这一章要解决的问题是,如果我要连续快速输入,内容,又不想多个 Sending 中的信息被合并,同时呢,我们又不想使用一些方式限制用户的输入,那么我们应该怎么做?"}),`
+`,t.jsx(e.p,{children:"我们在解决问题时,一定要对问题做出精准的分析,从而找到合适的解决方案。这里需求发生了一点变动,这里的变动就是,我不希望多个同时处于发送状态中的信息被回退重置。因此,我们要把状态拆分开,每一条信息各自维护自己的状态。"}),`
+`,t.jsxs(e.blockquote,{children:[`
+`,t.jsx(e.p,{children:"因此,通常情况下,我们会把需求进一步调整成为请求失败也不回退,而是给出重试按钮或者异常状态,这种情况下,就和乐观更新的需求产生了一点微妙的差异。基于这个基础,我们来重新思考实现方案"}),`
+`]}),`
+`,t.jsx(e.p,{children:"即然已经跟乐观更新产生了差异,那么我们就只需要借助常规的手段来实现即可。这里需要注意的是,虽然从需求上来说,我们可以不再思考回退,但是还需要保持乐观更新的 UI 特性。因此,在数据结构的设计上就需要有一些技巧。"}),`
+`,t.jsx(e.p,{children:"UI 需求的步骤为"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-javascript",children:`1、事件触发 -> UI 立即更新
+2、UI 更新的同时,发送请求,此时可以显示 Loading 状态
+3、请求成功之后,UI 确定更新,Loading 状态小时
+4、请求失败,显示失败状态
+`})}),`
+`,t.jsxs(e.blockquote,{children:[`
+`,t.jsx(e.p,{children:"我们注意看,这里虽然与我们之前描述的乐观更新有所差异,但是保留了大部分乐观更新的核心特征,只是没有失败回退的效果,而是失败重置或者显示失败状态"}),`
+`]}),`
+`,t.jsxs(e.p,{children:["首先,我们设计一个状态数据结构,该状态将要保留了乐观更新时提前展示的消息,以及一个 promise 用于请求。我们准备结合 ",t.jsx(e.code,{children:"use"})," + ",t.jsx(e.code,{children:"Suspense"})," 共同完成这个事情。"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-javascript",children:`action = {
+ newMessage: 'hello world',
+ promise: getMessage(newMessage)
+}
+`})}),`
+`,t.jsx(e.p,{children:"我们将会维护一个 action 组成的数组作为状态"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-javascript",children:`const [actions, updateActions] = useState([]);
+`})}),`
+`,t.jsxs(e.p,{children:["我们将结合 Suspense fallback 的特性,请求过程中通过 fallback 展示 ",t.jsx(e.code,{children:"newMessage"}),",然后请求成功之后再使用 promise 的请求结果来替换"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-javascript",children:`{actions.map((action, index) => (
+
{action.newMessage}(Seding...)}>
+
+
+))}
+`})}),`
+`,t.jsx(e.p,{children:"然后在 form 的 action 回调中,新增新的 action"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-javascript",children:`function formAction(formData) {
+ let newMessage = formData.get("message")
+ form.current.reset();
+ let action = {
+ newMessage,
+ promise: getMessage(newMessage)
+ }
+ updateActions((actions) => [...actions, action])
+}
+`})}),`
+`,t.jsx(e.p,{children:"完整代码请看右侧展示区域,代码演示如下"}),`
+`,t.jsx(A,{children:t.jsx(Hx,{})}),`
+`,t.jsxs(e.blockquote,{children:[`
+`,t.jsx(e.p,{children:"剩下一个需求,是请求失败展示错误或者重试,留一个小挑战,大家可以自行扩展一下"}),`
+`]})]})}function e9(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(gp,{...n})}):gp(n)}const n9={"App.js":J8,"api.js":P8};function t9(){return t.jsx(W,{files:n9,renderArticle:n=>t.jsx(e9,{components:{code:n}}),caseRender:t.jsx(Hx,{})})}const s9=`import { useOptimistic, useState, useTransition } from "react";
+import {reducer} from './reducer'
+import { likeApi } from "./api";
+import s from './index.module.css'
+
+export default function Index() {
+ const [like, setLike] = useState(false);
+ const [optimisticLike, dispatch] = useOptimistic(like, reducer);
+ const [isPending, startTransition] = useTransition()
+ const [end, setEnd] = useState()
+
+ function __clickHandler() {
+ if (isPending) return
+ let newState = !like;
+ startTransition(async () => {
+ dispatch(newState)
+ try {
+ let state = await likeApi(newState)
+ setLike(state)
+ setEnd(true)
+ } catch (e) {
+ setEnd(false)
+ }
+ })
+ }
+
+ let __cls = optimisticLike ? \`\${s.cen} \${s.active}\` : s.cen
+
+ return (
+
+
+
+ 状态:
+ {isPending && '请求中...'}
+ {!isPending && end === true && '请求成功'}
+ {!isPending && end === false && '请求失败'}
+
+
+ );
+}
+
+`,a9=`export async function likeApi(message) {
+ await new Promise((resolve, reject) => {
+ setTimeout(() => {
+ if (Math.random() > 0.1) {
+ resolve(message)
+ } else {
+ reject(message)
+ }
+ }, 1000)
+ });
+ return message;
+}
+`,l9=`export function reducer(state, newState) {
+ return newState
+}
+`,r9=`.star {
+ position: relative;
+ transform: scale(0.2);
+ height: 200px;
+ --active-color: #d5093c;
+ --normal-color: #cdcdcd;
+}
+.cen {
+ width: 200px;
+ height: 200px;
+ background-color: var(--normal-color);
+}
+.cen.active {
+ background-color: var(--active-color);
+ animation: 1s aj;
+}
+
+#lef {
+ border-radius: 100px;
+ position: absolute;
+ top:200px;
+ left: 300px;
+}
+#c {
+ transform: rotate(45deg);
+ position: absolute;
+ top:275px;
+ left: 375px;
+}
+#rig {
+ border-radius: 100px;
+ position: absolute;
+ top:200px;
+ left: 450px;
+}
+
+.loading {
+ text-align: center;
+}
+
+@keyframes aj {
+ 0%{transform: scale(1)rotate(45deg);}
+ 50%{transform: scale(1.1)rotate(45deg);}
+ 100%{transform: scale(1)rotate(45deg);}
+}
+`;function i9(n,e){return e}async function c9(n){return await new Promise((e,s)=>{setTimeout(()=>{Math.random()>.1?e(n):s(n)},1e3)}),n}const o9="_star_1fvk4_1",u9="_cen_1fvk4_8",d9="_active_1fvk4_13",m9="_aj_1fvk4_1",p9="_lef_1fvk4_1",f9="_c_1fvk4_8",h9="_rig_1fvk4_1",x9="_loading_1fvk4_37",dt={star:o9,cen:u9,active:d9,aj:m9,lef:p9,c:f9,rig:h9,loading:x9};function g9(){const[n,e]=f.useState(!1),[s,a]=f.useOptimistic(n,i9),[l,r]=f.useTransition(),[i,c]=f.useState();function o(){if(l)return;let d=!n;r(async()=>{a(d);try{let m=await c9(d);e(m),c(!0)}catch{c(!1)}})}let u=s?`${dt.cen} ${dt.active}`:dt.cen;return t.jsxs("div",{children:[t.jsxs("div",{className:dt.star,onClick:o,children:[t.jsx("div",{id:dt.lef,className:u}),t.jsx("div",{id:dt.c,className:u}),t.jsx("div",{id:dt.rig,className:u})]}),t.jsxs("div",{className:dt.loading,children:["状态:",l&&"请求中...",!l&&i===!0&&"请求成功",!l&&i===!1&&"请求失败"]})]})}function jp(n){const e={code:"code",h3:"h3",p:"p",pre:"pre",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.h3,{children:"一个复杂的案例"}),`
+`,t.jsx(e.p,{children:"再来实现一个比较常见的点赞按钮的交互逻辑。演示效果如上图所示。具体需求如下:"}),`
+`,t.jsx(e.p,{children:"当按钮处于灰色状态时,表示用户还未点赞该文章。点击之后,变成红色,表示点赞。"}),`
+`,t.jsx(e.p,{children:"当按钮处于红色状态时,表示用户已经点赞该文章。点击之后变成灰色,表示取消点赞。"}),`
+`,t.jsxs(e.p,{children:["解决方案与前面提到的完全一致,同时也结合了 ",t.jsx(e.code,{children:"useTransition"})," ,我们就不再一一分析步骤,直接展示完整代码"]}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`import { useOptimistic, useState, useTransition } from "react";
+import { likeApi } from "./api.js";
+import s from './index.module.css'
+
+export default function Index() {
+ const [like, setLike] = useState(false);
+ const [optimisticLike, updateLike] = useOptimistic(
+ like,
+ (state, newState) => newState
+ );
+ const [isPending, startTransition] = useTransition()
+ const [end, setEnd] = useState()
+
+ function __clickHandler() {
+ if (isPending) return
+ let newState = !like;
+ startTransition(async () => {
+ updateLike(newState)
+ try {
+ let state = await likeApi(newState)
+ setLike(state)
+ setEnd(true)
+ } catch (e) {
+ setEnd(false)
+ }
+ })
+ }
+
+ let __cls = optimisticLike ? \`\${s.cen} \${s.active}\` : s.cen
+
+ return (
+
+
+
+ 状态:
+ {isPending && '请求中...'}
+ {!isPending && end === true && '请求成功'}
+ {!isPending && end === false && '请求失败'}
+
+
+ );
+}
+`})}),`
+`,t.jsx(e.p,{children:"在 api 的请求中,我们可以通过判断随机数的大小来模拟请求失败时的表现。目前请求失败的概率是 10%"}),`
+`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`// api.js
+export async function likeApi(message) {
+ await new Promise((resolve, reject) => {
+ setTimeout(() => {
+ if (Math.random() > 0.1) {
+ resolve(message)
+ } else {
+ reject(message)
+ }
+ }, 1000)
+ });
+ return message;
+}
+`})}),`
+`,t.jsx(e.p,{children:"由于逻辑与之前几乎保持一致,因此本案例主要通过代码学习,未做详细讲解,完整代码如右侧所示。"})]})}function j9(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(jp,{...n})}):jp(n)}const v9={"App.js":s9,"api.js":a9,"reducer.js":l9,"index.module.css":r9};function b9(){return t.jsx(W,{files:v9,renderArticle:n=>t.jsx(j9,{components:{code:n}}),caseRender:t.jsx(g9,{})})}function vp(n){const e={a:"a",blockquote:"blockquote",code:"code",h3:"h3",hr:"hr",li:"li",p:"p",strong:"strong",ul:"ul",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.h3,{children:"完结感言"}),`
+`,t.jsxs(e.p,{children:["从 5 月 18 号左右,萌生了要写一本这样的付费小册,到现在 7 月 27 号,历时两个多月,",t.jsx(e.strong,{children:"这本小册终于正式完结啦。"})," 。:.゚ヽ(*´∀`)ノ゚.:。 由于我没有上班,几乎可以说是全职投入这本小册,因此看上去历时并不久,但是我经历的东西和过程确实非常多。"]}),`
+`,t.jsx(e.p,{children:"当我看着付费群里的 300 多人,从盈利的角度来说,在没有经历过大面积长时间的宣传,这本小册无疑是非常非常成功的。"}),`
+`,t.jsx(e.p,{children:"但是除此之外,在这本小册里,我还投入了对于新的学习方式和商业模式的探索,是对于我学习能力的一次重要考验,是对于我综合能力的一次有力验证,因此这本小册对我个人而言,意义非凡,它可能是一个重要的转折点。"}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"学习方法上的探索"}),`
+`,t.jsxs(e.p,{children:["大约从 2017 年开始,我就在",t.jsx(e.a,{href:"https://www.yuque.com/coreadvance/ar2my1/apk8cr",children:"从事 1V1 帮助别人提高前端技术能力的副业"}),",在这个长达 7 年的教学生涯中,我接触到了大量的形形色色的人。在学习的基础能力上,每个人都各不相同。有的人本身就具备较强的基础学习能力,稍加指点,就能够在职场中获得巨大的蜕变。而更多的却是连最基础的学习方法都没有掌握的人。"]}),`
+`,t.jsxs(e.p,{children:[t.jsx(e.strong,{children:"因此他们的一个共同的表现就是,他们在学习新知识的时候非常吃力。"})," 甚至还有很多人长时间不得其法。所以在这个过程中,我慢慢变得擅长为每个人量身定做适合的学习方法,并且在教学的过程中,把学习方法的培养变成重中之重。在这个基础之上,他们在跟我学习一段时间之后,就能够自己有足够的学习能力学习新的知识,从而具备自我成长的潜力。一个很好的结果就是,",t.jsx(e.strong,{children:"有好些人在技术方面的成就已经在我之上。"})]}),`
+`,t.jsxs(e.p,{children:["但是一个很大的问题就是,这样量身定做学习方法的方式并",t.jsx(e.strong,{children:"不具备普适价值"}),"。我有一个很大的感受就是,",t.jsx(e.strong,{children:"学霸的学习方法往往都是共通的,但是学霸之外,大家在学习上的痛,就长得奇形怪状,千奇百怪。"})," 同样的学习方法,在一个人身上闪闪发光,但是在另外一个人的身上,甚至毫无作用。甚至有的人由于自律的问题,需要在沟通的过程中不停的给他施加压力,否则就是长时间的自我懈怠。"]}),`
+`,t.jsx(e.p,{children:"更多的时候,还需要在学习过程中进行大量的心理辅导,帮助它解决各种原因导致的心里内耗。"}),`
+`,t.jsxs(e.p,{children:["因此我一直在思考,如何找到一种适合更多人的学习方式。虽然这种学习方法效果不如学霸级学习方法那么立竿见影,但是至少能够保证大多数人都学有所得。经过很多次思考、探索、实践,后来我才发现被广为病垢的",t.jsx(e.strong,{children:"填鸭式学习法"}),"可能才是我想要找的内容。"]}),`
+`,t.jsx(e.p,{children:"因为我在很长一段时间里,都一直认为填鸭式学习法是一种落后的,低效的学习方式,但是在带过很多学生之后发现,这可能是大多数人更愿意接受、并且效果性价比最高的一种学习方式。这样的思维转变对我来说非常困难。但是后来我重新审视了我自己长期以来带有偏见的观念。"}),`
+`,t.jsx(e.p,{children:"这个思考的灵感来源于付费群里的同学有人跟我说,很多时候不用非得彻底理解,先知道怎么用,用多了自然就理解了。因此在这个基础之上,我在文章内容中,内置了大量的实战案例,以及对应的,原样的代码。大概如下所示。"}),`
+`,t.jsx(A,{children:t.jsx(Dn,{files:{App:xx,"api.js":gx,List:vx,Tabs:bx,Skeleton:jx},caseRender:t.jsx(Nx,{})})}),`
+`,t.jsx(e.p,{children:"在大量这样的案例的支撑之下,有可能读者无法快速理解我想要传达的开发思维与架构思维,但是现成的案例可以让他快速将其运用到项目实践中。大量的运用实践可以帮助读者在实践中消化,在实践中理解与感悟,这样的障碍和瓶颈会降低很多。学习推进的进度可能会比较慢,但不会让人感觉窒息,不得其法。"}),`
+`,t.jsx(e.p,{children:"有真实案例运行的呈现方式,和只是贴代码或者截图很不一样。"}),`
+`,t.jsxs(e.ul,{children:[`
+`,t.jsx(e.li,{children:"这里的不同则来源于很多时候,贴的代码可能会存在缺少部分细节从而导致代码压根无法正确运行的情况,给学习者带来巨大的困扰。"}),`
+`,t.jsx(e.li,{children:"另外一方面则是排版的不规则和零散,会让学习者不得其法。"}),`
+`,t.jsx(e.li,{children:"真实案例能够更加直观的展示出来一篇文章的目标,在理解成本上也会降低很多。"}),`
+`,t.jsx(e.li,{children:"除此之外,很多只贴代码的表达,是可以不用经过验证的,许多人的文章虽然写出来了,但是并没有真的运行成功过,因此真实案例也是一次有效的验证成功,学习者不用担心这样的代码是无法运行的,减少了很多心里负担。"}),`
+`]}),`
+`,t.jsxs(e.p,{children:["也是到了后来,群友的正向学习反馈,让我更加理解了",t.jsx(e.strong,{children:"书读百遍,其义自现"}),"的含义。因此实际上这本小册很早就已经写完了,大概是 6 月 27 号发布了第一个版本。这一个月时间里,经历了许多群友的学习验证之后,我才敢写这篇完结感言,因为从大家的反馈中,我感受到了我的这一次探索是达到了我的预期成效的。"]}),`
+`,t.jsxs(e.blockquote,{children:[`
+`,t.jsxs(e.p,{children:["我写的每一本付费小册,都会深度践行我在学习方法上的思考和感悟,因此在小册开头,我都会花费大量的篇幅为大家讲解在学习方法上我是如何思考的,以便于大家能够快速明确应该以什么样的心态和理念去学习它,例如之前的",t.jsx(e.a,{href:"https://mp.weixin.qq.com/s?__biz=MzI4NjE3MzQzNg==&mid=2649867007&idx=1&sn=6443ff970cd077bbb50de74ce84afa06",children:"《React 哲学》"}),"、",t.jsx(e.a,{href:"https://appxw863qeq2150.h5.xiaoeknow.com/v1/goods/goods_detail/course_2Y46vk3Fhd2CF4CrB5qSNjW6A9R?",children:"《JavaScript 核心进阶》"}),",这本小册也是这样,可能未来我会将这些学习理念融合到一起,那么我的小册质量会越来越高"]}),`
+`]}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"商业模式上的思考"}),`
+`,t.jsxs(e.p,{children:["这本付费小册,是在我",t.jsx(e.a,{href:"https://mp.weixin.qq.com/s/W7jb77rSzML9vUbF-G0cPg",children:"探索微信小程序流量主模式失败"}),"之后的另外一种尝试。我希望能够在我获得收益的同时能够真实的帮助到他人,从而达到一种",t.jsx(e.strong,{children:"互利共赢"}),"的局面。"]}),`
+`,t.jsxs(e.p,{children:["因此,在付费群里面我多次与大家分享了这本小册可以使用在什么地方。首先要明确的一个就是,React 19 代表了一种新的开发方式,",t.jsx(e.strong,{children:"它本身在架构思维上是具有很强的先进性的"}),"。因此我们可以"]}),`
+`,t.jsxs(e.ul,{children:[`
+`,t.jsx(e.li,{children:"1、在面试中表达自己对 React19 的学习成果,从而展示自己对于新知识的热情"}),`
+`,t.jsx(e.li,{children:"2、在团队中做相关的技术分享,为年底绩效等铺垫内容"}),`
+`,t.jsx(e.li,{children:"3、彻底掌握 React19 的架构思维,重构团队中已经负重前行的项目,从而体现自己的团队贡献,为晋升打好基础,并且在这个过程中提升自己的项目架构能力"}),`
+`]}),`
+`,t.jsx(e.p,{children:"值得高兴的是,在这个一个月的时间里,确实有少数几位同学按照我的推荐去认真执行了,并且取得了不错的成效。因此,我希望这种商业模式能够健康的持续下去,以帮助到更多的人。"}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"技术方案上的思考"}),`
+`,t.jsx(e.p,{children:"从最开始萌生一个简单的想法,到最终将这个想法落地,其实中间还需要走很长的路。因此制作一本这样的付费小册的想法只需要一个一瞬间,但是真实要落地下来,面临的困难却非常多。如何实现它成为了一个难题。"}),`
+`,t.jsxs(e.p,{children:["这其中比较困难的就在于,如何让演示案例与演示代码",t.jsx(e.strong,{children:"共存"}),",并且案例运行的代码与展示的代码是同一份。这样就极大程度的确保了",t.jsx(e.strong,{children:"一致性"}),"。"]}),`
+`,t.jsx(e.p,{children:"为了解决这个难题,我做了一些尝试"}),`
+`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"一、调研现有成熟的技术网站"})}),`
+`,t.jsx(e.p,{children:"我调研了大多数常用的技术博客论坛,例如大家熟知的知乎、掘金、语雀、以及国外的一些平台,Medium,github 等,但是他们都无法支持我的想法,当然,我本身对我的想法能够被满足也不报太大的希望。"}),`
+`,t.jsx(e.p,{children:t.jsxs(e.strong,{children:["二、与 ",t.jsx(e.code,{children:"mdnice"})," 核心开发大鹏沟通,是否能够新增一些需求满足我的想法"]})}),`
+`,t.jsxs(e.p,{children:["大鹏是我在做自由职业时认识的一位大佬,他的个人作品 ",t.jsx(e.code,{children:"mdnice"})," 在 md 编辑器上的积累已经非常成熟,因此,因此我第二个想到的是看看能不能在这个基础之上做一些扩展,来满足我的需求,不过后续我们相互沟通之后发现我的这种需求放到他现有的体系里不是很合适,因为 ",t.jsx(e.code,{children:"mdnice"})," 主要面向的是针对公众号的布局开发。"]}),`
+`,t.jsx(e.p,{children:"不过在后续的沟通过程中,我们又迸发了许多新的想法,并且后续我可能也会参与 mdnice 的一些开发工作,发挥我的独特优势进一步补全我们的想法,让 mdnice 在商业化的道路上变得更加完善。"}),`
+`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"三、调研现有的博客系统,例如 dumi、vitepress、Rspress"})}),`
+`,t.jsx(e.p,{children:"这些都是非常成熟的解决方案。并且内置的 mdx 可以比较轻松的完成我的构想。但是由于我的设想中,定制化程度太高,在花费了俩三天时间学习 vitepress 的使用过程中,感觉有一些想要的东西还是实现不了,因此最终决定放弃使用别人现有的方案,而是基于 mdx 自己组装一套方案出来。"}),`
+`,t.jsx(e.p,{children:t.jsx(e.strong,{children:"四、调研现有的技术方案"})}),`
+`,t.jsx(e.p,{children:"得益于 React 生态的强大,我原本认为会很困难的事情,没想到都有好几种对应的解决方案。"}),`
+`,t.jsx(e.p,{children:"这里边需要解决问题包括"}),`
+`,t.jsxs(e.ul,{children:[`
+`,t.jsx(e.li,{children:"1、md 文件格式的渲染。可选的方案有 react-markdown、mdx 等"}),`
+`,t.jsx(e.li,{children:"2、代码高亮 可选的方案有 react-syntax-highlighter、remarkjs、highlight 等"}),`
+`,t.jsx(e.li,{children:"3、内置编辑器有 Monaco Editor、Mirror Editor 等"}),`
+`,t.jsx(e.li,{children:"4、案例热更新方案 Sandpack"}),`
+`]}),`
+`,t.jsx(e.p,{children:"我花了很大的精力去分别验证这些解决方案的可行性,最终经过试用淘汰,演变成目前的状态,虽然有的方案最终我并没有采用,但是在这个过程中我对他们的运用已经非常熟练。"}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"对我综合能力的考验"}),`
+`,t.jsx(e.p,{children:"自己开发一个网站,很多时候最考验我的,反而是综合能力。例如在做这个网站的过程中,我自己为整个网站摸索了 UI 风格,并且为每一个案例都精心准备了视觉效果良好的 UI 设计。例如这个案例"}),`
+`,t.jsx(A,{children:t.jsx(dx,{})}),`
+`,t.jsx(e.p,{children:"我自己设计并实现了常用的 UI 组件。"}),`
+`,t.jsx(e.p,{children:"我自己从零到一完成了移动端的适配。"}),`
+`,t.jsx(e.p,{children:"我自己重新设计了整个网站的架构,以便于扩展到下一份付费小册中去。"}),`
+`,t.jsx(e.p,{children:"我自己招商引资,寻找了几个赞助商进行合作。"}),`
+`,t.jsx(e.p,{children:"我自己负责宣传,虽然目前宣传力度很微弱,但是也在某种程度上让几百位同学认同了我的这本小册的价值。"}),`
+`,t.jsx(e.p,{children:"所有的事情从头到尾都需要一个人来做,这对于我综合能力的要求是非常高的。事实上在刚开始的时候,我并没有很大的预期他能够取得成功,包括现在我也没有很大的底气能够得到更多人的认可。每一次的宣传都是小心翼翼的试探,很担心最终的结果是无人理会,只是我单方面的自娱自乐。"}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"对于最佳实践的探索"}),`
+`,t.jsxs(e.p,{children:["很显然,React 19 的出现,意味着一种",t.jsx(e.strong,{children:"新的开发思维"}),"被正式抬到了大众眼前。在这种新的开发思维之下,我也是从头开始尝试与学习,那么如何找到最佳实践本身就是一件充满挑战的事情。"]}),`
+`,t.jsx(e.p,{children:"因此,在写每一个案例时,我都会非常小心的去求证和对比,感受每一种写法在开发思维上所带来的提升。这是最难的一个方向。因此在写这本小册之前,我会把每一个知识点我的学习体会、过程、疑问都放到公众号中与大家进行探讨,在多方求证之后,确定了这样的做法是目前的最佳实践之后,才形成完整的教学文章。"}),`
+`,t.jsxs(e.p,{children:["因此,在我的个人公众号",t.jsx(e.code,{children:"这波能反杀"}),"中,你能看到一系列关于 React 19 的文章。幸运的是,在公众号中,我遇到了很多顶尖的大佬跟我探讨学习心得。这才确保了这本付费小册的质量是非常高的。感谢每一位参与过探讨帮助我成长的大佬。"]}),`
+`,t.jsx(e.hr,{}),`
+`,t.jsx(e.h3,{children:"最后"}),`
+`,t.jsxs(e.p,{children:["学习与进步是一个需要持续不断做下去的事情。在写这本小册的过程中,我也收获了大量的新的知识。我的进步非常大。很庆幸我自己可以",t.jsx(e.strong,{children:"很纯粹"}),"的做完这个事情。也希望自己能够长期保持下去,摈弃掉一些外界乱七八糟的杂念与内耗,持续学习,持续进步,以此与大家共勉。"]}),`
+`,t.jsxs(e.p,{children:["小册目前价格:",t.jsx(e.strong,{children:"30 元"})]}),`
+`,t.jsxs(e.p,{children:["购买方式:添加我的微信 ",t.jsx(e.code,{children:"icanmeetu"})," 微信转账即可,获得激活码即可阅读所有内容"]})]})}function y9(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(vp,{...n})}):vp(n)}function bp(){return t.jsx(rt,{isAuth:!1,renderArticle:n=>t.jsx(y9,{components:{code:n}})})}const Vx=[{path:"formaction/end",name:"附录:完结感言",component:bp},{type:"tip",name:"前言"},{path:"index",name:"1、开发方式变革",component:F5},{path:"layers",name:"2、学习的三个层次",component:m4},{path:"create",name:"3、创建项目",component:b4},{type:"tip",name:"use(promise)"},{path:"use/base",name:"3、use 基础知识",component:z4},{path:"use/suspense",name:"4、Suspense",component:ty},{path:"use/update",name:"5、点击更新数据",component:vy},{path:"use/initialize",name:"6、初始化请求并更新",component:Ly},{path:"use/updatetolist",name:"7、请求并新增到列表",component:Wy},{path:"use/loadmore",name:"8、分页列表加载更多",component:u3},{path:"use/search",name:"9、搜索",component:N3},{path:"use/tabs",name:"10、tab 简单切换",component:M3,label:"1"},{path:"use/tabshard",name:"11、tab 缓存切换",component:Z3,label:"2"},{path:"use/fromchildren",name:"12、父级获取数据",component:a6,label:"3"},{path:"use/nest",name:"13、Suspense 嵌套",component:h6},{path:"use/react16",name:"14、在低版本中使用",component:k6},{type:"tip",name:"use(context)"},{path:"use/ref",name:"15、ref 调整",component:V6},{path:"use/modal",name:"16、自定义弹窗组件",component:P6},{path:"use/contextmodify",name:"17、弹窗中更改内容",component:r7},{path:"use/skinswitch",name:"18、皮肤切换次数",component:g7},{type:"tip",name:"并发 API"},{path:"use/deferred",name:"19、useDeferredValue",component:L7},{path:"use/transition",name:"20、useTransition",component:F7},{type:"tip",name:"Compiler"},{path:"use/importwith19",name:"21、React 19 中引入",component:sN},{path:"use/importwithlower",name:"22、低版本中引入",component:cN},{path:"use/importwithwebpack",name:"23、webpack中引入",component:pN},{path:"use/compilerbase",name:"24、Compiler 编译原理",component:vN},{path:"use/compilercount",name:"25、收益分析-递增",component:_N},{path:"use/compilerexpensive",name:"26、收益分析-耗时子组件",component:TN},{path:"use/compilertabs",name:"27、收益分析-tabs",component:JN},{type:"tip",name:"form action"},{path:"formaction/base",name:"28、form 基础",component:FN},{path:"formaction/formdata",name:"29、FormData 基础",component:n8},{path:"formaction/action",name:"30、form action",component:c8},{path:"formaction/useformstatus",name:"31、useFormStatus",component:f8},{path:"formaction/useactionstate",name:"32、useActionState",component:C8},{path:"formaction/useoptimistic",name:"33、useOptimistic",component:B8,label:"1"},{path:"formaction/useoptimistic2",name:"33、useOptimistic",component:Q8,label:"2"},{path:"formaction/useoptimistic3",name:"33、useOptimistic",component:t9,label:"3"},{path:"formaction/useoptimistic4",name:"34、复杂案例",component:b9,label:"4"},{type:"tip",name:"后续闲谈"},{path:"formaction/end",name:"35、完结感言",component:bp}];function yp(){return t.jsx("div",{children:Vx.map((n,e)=>n.type==="tip"?t.jsx("div",{className:"mx-2 text-sm px-4 text-gray-400 first:pt-0 pb-4 pt-8",children:n.name},`z-${e}`):t.jsx(Cn,{className:"text-gray-700",activeName:"text-blue-700",to:n.path,children:t.jsxs("li",{className:"mx-2 px-4 py-3 transition hover:bg-blue-100 text-sm flex items-center justify-between hover:text-blue-700 rounded",children:[t.jsxs("div",{className:"flex items-center",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-4",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"})}),t.jsx("div",{className:"ml-2 line-clamp-1",children:n.name})]}),n.label?t.jsx(F0,{label:n.label,primary:!0,className:"ml-2"}):null]},n.path)},n.path))})}function N9(){const n=ru(),e=f.useRef(new Map),s=iu(),{pathname:a}=is();f.useReducer(r=>!r)[1];const l=f.useRef(null);return e.current.has(a)||e.current.set(a,s),f.useEffect(()=>{location.pathname==="/tutorial"&&n("index")},[a]),t.jsxs("div",{className:"pt-16 md:flex",children:[t.jsx(le,{signal:!0,className:"fixed bottom-6 right-6 z-40 md:hidden",onClick:()=>l.current.show(),children:"目录"}),t.jsx(ti,{ref:l,onClose:()=>l.current.close(),children:t.jsx("div",{className:"bg-white h-full py-4 overflow-scroll",children:t.jsx(yp,{})})}),t.jsxs("nav",{className:"hidden md:block w-60 sticky top-16 h-[calc(100vh_-_5rem)] overflow-y-auto bg-hei",children:[t.jsxs("div",{className:"flex items-center px-4 py-8",children:[t.jsx("div",{className:"flex items-center justify-between p-1 border mr-3 rounded-md border-cyan-200",children:t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",className:"size-5 text-cyan-500",children:t.jsx("path",{fillRule:"evenodd",d:"M19.5 21a3 3 0 0 0 3-3V9a3 3 0 0 0-3-3h-5.379a.75.75 0 0 1-.53-.22L11.47 3.66A2.25 2.25 0 0 0 9.879 3H4.5a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h15Zm-6.75-10.5a.75.75 0 0 0-1.5 0v4.19l-1.72-1.72a.75.75 0 0 0-1.06 1.06l3 3a.75.75 0 0 0 1.06 0l3-3a.75.75 0 1 0-1.06-1.06l-1.72 1.72V10.5Z",clipRule:"evenodd"})})}),t.jsxs("div",{className:"text-sm",children:[t.jsx("div",{children:"当前版本"}),t.jsx("div",{className:"text-gray-500",children:"React@19.0.0-rc"})]})]}),t.jsx("ul",{className:"border-r h-[calc(100vh_-_9rem_-_35px)] overflow-y-auto pb-8",children:t.jsx(yp,{})})]}),t.jsx("div",{className:"md:flex-1 md:w-[calc(100vw_-_15rem)] p-4 md:p-8 min-h-[calc(100vh_-_5rem)] box-border",children:Array.from(e.current).map(([r,i])=>t.jsx("div",{style:{display:a===r?"block":"none"},children:i},r))})]})}function Np(n){const e={code:"code",p:"p",pre:"pre",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"基础使用"}),`
`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-html",children:`
`})}),`
`,t.jsx("div",{children:t.jsx(le,{children:"hello button"})}),`
@@ -8097,18 +8794,18 @@ export async function addToCart(prevState, queryData) {
`})}),`
-`,t.jsx(T,{children:t.jsxs("div",{className:"mt-4 flex justify-around",children:[t.jsx(le,{danger:!0,children:"Danger"}),t.jsx(le,{primary:!0,children:"Primary"}),t.jsx(le,{success:!0,children:"Success"}),t.jsx(le,{signal:!0,children:"Signal"})]})}),`
+`,t.jsx(A,{children:t.jsxs("div",{className:"mt-4 flex justify-around",children:[t.jsx(le,{danger:!0,children:"Danger"}),t.jsx(le,{primary:!0,children:"Primary"}),t.jsx(le,{success:!0,children:"Success"}),t.jsx(le,{signal:!0,children:"Signal"})]})}),`
`,t.jsx(e.p,{children:"大小"}),`
`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-html",children:`
`})}),`
-`,t.jsxs(T,{children:[t.jsxs("div",{className:"mt-4",children:[t.jsx(le,{danger:!0,sm:!0,className:"mr-4",children:"Danger"}),t.jsx(le,{danger:!0,className:"mr-4",children:"Danger"}),t.jsx(le,{danger:!0,lg:!0,children:"Danger"})]}),t.jsxs("div",{className:"mt-4",children:[t.jsx(le,{success:!0,lg:!0,className:"mr-4",children:"Success"}),t.jsx(le,{success:!0,className:"mr-4",children:"Success"}),t.jsx(le,{success:!0,sm:!0,children:"Success"})]}),t.jsxs("div",{className:"mt-4",children:[t.jsx(le,{primary:!0,sm:!0,className:"mr-4",children:"Primary"}),t.jsx(le,{primary:!0,className:"mr-4",children:"Primary"}),t.jsx(le,{primary:!0,lg:!0,children:"Primary"})]}),t.jsxs("div",{className:"mt-4",children:[t.jsx(le,{signal:!0,lg:!0,className:"mr-4",children:"Signal"}),t.jsx(le,{signal:!0,className:"mr-4",children:"Signal"}),t.jsx(le,{signal:!0,sm:!0,children:"Signal"})]})]}),`
+`,t.jsxs(A,{children:[t.jsxs("div",{className:"mt-4",children:[t.jsx(le,{danger:!0,sm:!0,className:"mr-4",children:"Danger"}),t.jsx(le,{danger:!0,className:"mr-4",children:"Danger"}),t.jsx(le,{danger:!0,lg:!0,children:"Danger"})]}),t.jsxs("div",{className:"mt-4",children:[t.jsx(le,{success:!0,lg:!0,className:"mr-4",children:"Success"}),t.jsx(le,{success:!0,className:"mr-4",children:"Success"}),t.jsx(le,{success:!0,sm:!0,children:"Success"})]}),t.jsxs("div",{className:"mt-4",children:[t.jsx(le,{primary:!0,sm:!0,className:"mr-4",children:"Primary"}),t.jsx(le,{primary:!0,className:"mr-4",children:"Primary"}),t.jsx(le,{primary:!0,lg:!0,children:"Primary"})]}),t.jsxs("div",{className:"mt-4",children:[t.jsx(le,{signal:!0,lg:!0,className:"mr-4",children:"Signal"}),t.jsx(le,{signal:!0,className:"mr-4",children:"Signal"}),t.jsx(le,{signal:!0,sm:!0,children:"Signal"})]})]}),`
`,t.jsx(e.p,{children:"完整源码"}),`
-`,t.jsx(In,{files:{"index.jsx":gl}})]})}function bN(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(gp,{...n})}):gp(n)}function yN(){return t.jsx(Ot,{renderArticle:n=>t.jsx(bN,{components:{code:n}}),isGiscus:!1})}function vp(n){const e={code:"code",p:"p",pre:"pre",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"Input 基础使用"}),`
+`,t.jsx(Dn,{files:{"index.jsx":jl}})]})}function S9(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Np,{...n})}):Np(n)}function w9(){return t.jsx(rt,{renderArticle:n=>t.jsx(S9,{components:{code:n}}),isGiscus:!1})}function Sp(n){const e={code:"code",p:"p",pre:"pre",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"Input 基础使用"}),`
`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-html",children:`
`})}),`
-`,t.jsx(T,{children:t.jsx(yn,{placeholder:"请输入任意内容"})})]})}function NN(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(vp,{...n})}):vp(n)}function SN(){return t.jsx(Ot,{renderArticle:n=>t.jsx(NN,{components:{code:n}}),isGiscus:!1})}function wN(n){return t.jsx("div",{className:"border border-blue-300 shadow rounded-md p-4 max-w-sm w-full mx-auto",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}function jp(n){const e={code:"code",p:"p",pre:"pre",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"基础使用"}),`
+`,t.jsx(A,{children:t.jsx(yn,{placeholder:"请输入任意内容"})})]})}function _9(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(Sp,{...n})}):Sp(n)}function k9(){return t.jsx(rt,{renderArticle:n=>t.jsx(_9,{components:{code:n}}),isGiscus:!1})}function C9(n){return t.jsx("div",{className:"border border-blue-300 shadow rounded-md p-4 max-w-sm w-full mx-auto",children:t.jsxs("div",{className:"animate-pulse flex space-x-4",children:[t.jsx("div",{className:"rounded-full bg-slate-200 h-10 w-10"}),t.jsxs("div",{className:"flex-1 space-y-6 py-1",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded"}),t.jsxs("div",{className:"space-y-3",children:[t.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-2"}),t.jsx("div",{className:"h-2 bg-slate-200 rounded col-span-1"})]}),t.jsx("div",{className:"h-2 bg-slate-200 rounded"})]})]})]})})}function wp(n){const e={code:"code",p:"p",pre:"pre",...n.components};return t.jsxs(t.Fragment,{children:[t.jsx(e.p,{children:"基础使用"}),`
`,t.jsx(e.pre,{children:t.jsx(e.code,{className:"language-js",children:`
`})}),`
-`,t.jsx(wN,{})]})}function _N(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(jp,{...n})}):jp(n)}function kN(){return t.jsx(Ot,{renderArticle:n=>t.jsx(_N,{components:{code:n}}),isGiscus:!1})}const wx=[{type:"tip",name:"基础组件"},{path:"button",name:"1、按钮 Button",component:yN},{path:"input",name:"2、输入框 Input",component:SN},{path:"skeleton",name:"2、骨架屏 Skeleton",component:kN}];function CN(){const n=ru(),e=h.useRef(new Map),s=cu(),{pathname:a}=cs(),l=h.useReducer(r=>!r)[1];return e.current.has(a)||e.current.set(a,s),h.useEffect(()=>{location.pathname==="/component"&&n("button")},[a]),t.jsxs("div",{className:"pt-16 flex",children:[t.jsxs("nav",{className:"hidden md:block w-60 sticky top-16 h-[calc(100vh_-_5rem)] overflow-y-auto bg-hei",children:[t.jsxs("div",{className:"flex items-center px-4 py-8",children:[t.jsx("div",{className:"flex items-center justify-between p-1 border mr-3 rounded-md border-cyan-200",children:t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",className:"size-5 text-cyan-500",children:t.jsx("path",{fillRule:"evenodd",d:"M19.5 21a3 3 0 0 0 3-3V9a3 3 0 0 0-3-3h-5.379a.75.75 0 0 1-.53-.22L11.47 3.66A2.25 2.25 0 0 0 9.879 3H4.5a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h15Zm-6.75-10.5a.75.75 0 0 0-1.5 0v4.19l-1.72-1.72a.75.75 0 0 0-1.06 1.06l3 3a.75.75 0 0 0 1.06 0l3-3a.75.75 0 1 0-1.06-1.06l-1.72 1.72V10.5Z",clipRule:"evenodd"})})}),t.jsxs("div",{className:"text-sm",children:[t.jsx("div",{children:"当前版本"}),t.jsx("div",{className:"text-gray-500",children:"React@19.0.0-rc"})]})]}),t.jsx("ul",{className:"border-r h-[calc(100vh_-_9rem_-_35px)] overflow-y-auto pb-8",children:wx.map((r,c)=>r.type==="tip"?t.jsx("div",{className:"mx-2 text-sm px-4 text-gray-400 first:pt-0 pb-4 pt-8",children:r.name},`z-${c}`):t.jsx(Cn,{className:"text-gray-700",activeName:"text-blue-700",to:r.path,onClick:l,children:t.jsxs("li",{className:"mx-2 px-4 py-3 transition hover:bg-blue-100 text-sm flex items-center justify-between hover:text-blue-700 rounded",children:[t.jsxs("div",{className:"flex items-center",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-4",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"})}),t.jsx("div",{className:"ml-2 line-clamp-1",children:r.name})]}),r.label?t.jsx(Q0,{label:r.label,primary:!0,className:"ml-2"}):null]},r.path)},r.path))})]}),t.jsx("div",{className:"w-full md:w-[calc(100vw_-_15rem)] p-8 min-h-[calc(100vh_-_5rem)] box-border",children:Array.from(e.current).map(([r,c])=>t.jsx("div",{style:{display:a===r?"block":"none"},children:c},r))})]})}function RN(){return t.jsxs("div",{children:[t.jsx(jj,{className:"line"}),t.jsx(vv,{})]})}function EN(){return t.jsx(bv,{children:t.jsxs(dt,{path:"/",element:t.jsx(RN,{}),children:[t.jsx(dt,{index:!0,element:t.jsx(y5,{})}),t.jsx(dt,{path:"tutorial",element:t.jsx(jN,{}),children:Sx.filter(n=>!!n.path).map((n,e)=>t.jsx(dt,{path:n.path,element:t.jsx(h.Suspense,{fallback:t.jsx(nm,{}),children:t.jsx(n.component,{})})},n.path))}),t.jsx(dt,{path:"component",element:t.jsx(CN,{}),children:wx.filter(n=>!!n.path).map((n,e)=>t.jsx(dt,{path:n.path,element:t.jsx(h.Suspense,{fallback:t.jsx(nm,{}),children:t.jsx(n.component,{})})},n.path))}),t.jsx(dt,{path:"*",element:t.jsx("div",{children:"nothingsdfsdfsdf"})})]})})}M2.createRoot(document.getElementById("root")).render(t.jsx(h.StrictMode,{children:t.jsx(Cv,{children:t.jsx(EN,{})})}));
+`,t.jsx(C9,{})]})}function R9(n={}){const{wrapper:e}=n.components||{};return e?t.jsx(e,{...n,children:t.jsx(wp,{...n})}):wp(n)}function M9(){return t.jsx(rt,{renderArticle:n=>t.jsx(R9,{components:{code:n}}),isGiscus:!1})}const Xx=[{type:"tip",name:"基础组件"},{path:"button",name:"1、按钮 Button",component:w9},{path:"input",name:"2、输入框 Input",component:k9},{path:"skeleton",name:"2、骨架屏 Skeleton",component:M9}];function E9(){const n=ru(),e=f.useRef(new Map),s=iu(),{pathname:a}=is(),l=f.useReducer(r=>!r)[1];return e.current.has(a)||e.current.set(a,s),f.useEffect(()=>{location.pathname==="/component"&&n("button")},[a]),t.jsxs("div",{className:"pt-16 flex",children:[t.jsxs("nav",{className:"hidden md:block w-60 sticky top-16 h-[calc(100vh_-_5rem)] overflow-y-auto bg-hei",children:[t.jsxs("div",{className:"flex items-center px-4 py-8",children:[t.jsx("div",{className:"flex items-center justify-between p-1 border mr-3 rounded-md border-cyan-200",children:t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",className:"size-5 text-cyan-500",children:t.jsx("path",{fillRule:"evenodd",d:"M19.5 21a3 3 0 0 0 3-3V9a3 3 0 0 0-3-3h-5.379a.75.75 0 0 1-.53-.22L11.47 3.66A2.25 2.25 0 0 0 9.879 3H4.5a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h15Zm-6.75-10.5a.75.75 0 0 0-1.5 0v4.19l-1.72-1.72a.75.75 0 0 0-1.06 1.06l3 3a.75.75 0 0 0 1.06 0l3-3a.75.75 0 1 0-1.06-1.06l-1.72 1.72V10.5Z",clipRule:"evenodd"})})}),t.jsxs("div",{className:"text-sm",children:[t.jsx("div",{children:"当前版本"}),t.jsx("div",{className:"text-gray-500",children:"React@19.0.0-rc"})]})]}),t.jsx("ul",{className:"border-r h-[calc(100vh_-_9rem_-_35px)] overflow-y-auto pb-8",children:Xx.map((r,i)=>r.type==="tip"?t.jsx("div",{className:"mx-2 text-sm px-4 text-gray-400 first:pt-0 pb-4 pt-8",children:r.name},`z-${i}`):t.jsx(Cn,{className:"text-gray-700",activeName:"text-blue-700",to:r.path,onClick:l,children:t.jsxs("li",{className:"mx-2 px-4 py-3 transition hover:bg-blue-100 text-sm flex items-center justify-between hover:text-blue-700 rounded",children:[t.jsxs("div",{className:"flex items-center",children:[t.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"size-4",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"})}),t.jsx("div",{className:"ml-2 line-clamp-1",children:r.name})]}),r.label?t.jsx(F0,{label:r.label,primary:!0,className:"ml-2"}):null]},r.path)},r.path))})]}),t.jsx("div",{className:"w-full md:w-[calc(100vw_-_15rem)] p-8 min-h-[calc(100vh_-_5rem)] box-border",children:Array.from(e.current).map(([r,i])=>t.jsx("div",{style:{display:a===r?"block":"none"},children:i},r))})]})}function A9(){return t.jsxs("div",{children:[t.jsx(zv,{className:"line"}),t.jsx(Bj,{})]})}function T9(){return t.jsx(Uj,{children:t.jsxs(pt,{path:"/",element:t.jsx(A9,{}),children:[t.jsx(pt,{index:!0,element:t.jsx(B5,{})}),t.jsx(pt,{path:"tutorial",element:t.jsx(N9,{}),children:Vx.filter(n=>!!n.path).map((n,e)=>t.jsx(pt,{path:n.path,element:t.jsx(f.Suspense,{fallback:t.jsx(nm,{}),children:t.jsx(n.component,{})})},n.path))}),t.jsx(pt,{path:"component",element:t.jsx(E9,{}),children:Xx.filter(n=>!!n.path).map((n,e)=>t.jsx(pt,{path:n.path,element:t.jsx(f.Suspense,{fallback:t.jsx(nm,{}),children:t.jsx(n.component,{})})},n.path))}),t.jsx(pt,{path:"*",element:t.jsx("div",{children:"nothingsdfsdfsdf"})})]})})}P2.createRoot(document.getElementById("root")).render(t.jsx(f.StrictMode,{children:t.jsx(Yj,{children:t.jsx(T9,{})})}));
diff --git a/index.html b/index.html
index 23e02e1..c7c5b6e 100644
--- a/index.html
+++ b/index.html
@@ -6,8 +6,8 @@
React 19 - 这波能反杀
-
-
+
+
diff --git a/stats.html b/stats.html
index 52c9a86..aa91433 100644
--- a/stats.html
+++ b/stats.html
@@ -4822,7 +4822,7 @@