[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-react-weekly-form-actions-pending-state":44,"search-suggestions":399,"related-react-weekly-form-actions-pending-state":437,"code:jsx:true:dca1da":559,"code:jsx:true:ht1ztn":560,"code:jsx:true:3tnf4n":561,"code:jsx:true:uuesq1":562,"code:jsx:true:s2g2w9":563,"code:jsx:true:ts9v23":564,"comments-01a08734-5c8d-73c5-95d1-e6b4ae366c25":565},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":74,"playground":76,"body":78,"bodyMd":371,"seo":372,"translationGroupId":374,"series":375,"podcastUrl":48,"verticalId":5,"thread":387,"assessments":389,"translations":395,"quiz":397},"01a08734-5c8d-73c5-95d1-e6b4ae366c25","react-weekly-form-actions-pending-state","React Form Actions: useActionState & useFormStatus Guide",null,"How React 19 form Actions manage pending state automatically — useActionState, useFormStatus, and useOptimistic explained with the bug they replace.","\u002Fmedia\u002Fcovers\u002Freact-weekly-form-actions-pending-state.png",13,"2026-09-12T10:23:52.928Z",9,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"react","React",[64,65,68,71],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},"javascript","Javascript",{"slug":69,"name":70,"color":48},"tutorial","Tutorial",{"slug":72,"name":73,"color":48},"webdev","Webdev",{"assessments":75},1,{"slug":46,"title":77},"React form Actions — pending state playground",{"blocks":79,"version":75},[80,84,87,90,95,98,107,110,113,116,130,133,136,142,145,148,151,154,158,161,165,168,172,175,179,182,185,189,192,196,199,202,206,209,213,216,219,222,225,228,231,234,242,245,253,256,260,263,266,269,273,276,280,283,286,289,292,320,324,327,330,333,336,339,342,350,353,356,359,362,365],{"id":81,"html":82,"type":83},"b1","\u003Cp>You&#39;ve built this submit button before. \u003Ccode>useState(false)\u003C\u002Fcode> for \u003Ccode>isSubmitting\u003C\u002Fcode>, set it \u003Ccode>true\u003C\u002Fcode> at the top of the handler, \u003Ccode>await\u003C\u002Fcode> the request, set it back \u003Ccode>false\u003C\u002Fcode> in a \u003Ccode>finally\u003C\u002Fcode>. It works — until a teammate adds an early \u003Ccode>return\u003C\u002Fcode> inside a validation branch that skips the \u003Ccode>finally\u003C\u002Fcode>, or a request fails in a way your \u003Ccode>catch\u003C\u002Fcode> didn&#39;t anticipate, and now the button is disabled forever. The state and the reality it&#39;s supposed to describe have quietly come apart.\u003C\u002Fp>","paragraph",{"id":85,"html":86,"type":83},"b2","\u003Cp>This is episode three of \u003Cstrong>React Deep Dive\u003C\u002Fstrong>, a weekly series on what React itself decides, not JavaScript with a React import at the top. The first two episodes covered \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-re-render-vs-remount-what-actually-triggers-each-5fok\">re-render vs. remount\u003C\u002Fa> and \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-compiler-10-what-usememo-you-can-delete-hgm\">what the React Compiler automates\u003C\u002Fa> — useful background, neither required here. This episode is about form \u003Cstrong>Actions\u003C\u002Fstrong>: \u003Ccode>useActionState\u003C\u002Fcode>, \u003Ccode>useFormStatus\u003C\u002Fcode>, and \u003Ccode>useOptimistic\u003C\u002Fcode>, and the specific problem they solve, which isn&#39;t &quot;nicer syntax for async handlers&quot; — it&#39;s removing a piece of state you were maintaining by hand and could get wrong.\u003C\u002Fp>",{"id":88,"html":89,"type":83},"b3","\u003Cp>This article is written against \u003Cstrong>React 19.2\u003C\u002Fstrong> (verified 19.2.8, the current npm release as of this writing) and assumes React 19-era function components and hooks throughout.\u003C\u002Fp>",{"id":91,"html":92,"text":93,"type":94,"level":31},"b4","What You&#39;ll Learn","What You'll Learn","heading",{"id":96,"html":97,"type":83},"b5","\u003Cp>By the end of this article you&#39;ll be able to:\u003C\u002Fp>",{"id":99,"type":100,"items":101,"ordered":18},"b6","list",[102,103,104,105,106],"Explain why a form Action&#39;s pending state can&#39;t drift out of sync the way a hand-written \u003Ccode>isSubmitting\u003C\u002Fcode> flag can","Use \u003Ccode>useActionState\u003C\u002Fcode> to bundle an action, its result, and its pending flag into one hook","Read pending status in a child component with \u003Ccode>useFormStatus\u003C\u002Fcode>, without prop-drilling a boolean","Layer \u003Ccode>useOptimistic\u003C\u002Fcode> on top for instant UI feedback while an action is in flight","Avoid the one real trap: throwing inside an action instead of returning an error as state",{"id":108,"html":109,"text":109,"type":94,"level":31},"b7","Who This Is For",{"id":111,"html":112,"type":83},"b8","\u003Cp>You&#39;ve written function components with \u003Ccode>useState\u003C\u002Fcode> and \u003Ccode>useEffect\u003C\u002Fcode>, and you&#39;ve handled at least one form submission with a manual loading flag. No prior exposure to React 19&#39;s Actions is assumed.\u003C\u002Fp>",{"id":114,"html":115,"text":115,"type":94,"level":31},"b9","Table of Contents",{"id":117,"type":100,"items":118,"ordered":18},"b10",[119,120,121,122,123,124,125,126,127,128,129],"\u003Ca href=\"#the-problem-a-pending-state-you-track-by-hand\">The Problem: A Pending State You Track By Hand\u003C\u002Fa>","\u003Ca href=\"#the-mental-model-pending-is-derived-not-stored\">The Mental Model: Pending Is Derived, Not Stored\u003C\u002Fa>","\u003Ca href=\"#stage-1-a-plain-action-on-a-form\">Stage 1: A Plain Action on a \u003Ccode>&lt;form&gt;\u003C\u002Fcode>\u003C\u002Fa>","\u003Ca href=\"#stage-2-useactionstate-for-result--pending\">Stage 2: \u003Ccode>useActionState\u003C\u002Fcode> for Result + Pending\u003C\u002Fa>","\u003Ca href=\"#stage-3-useformstatus-in-a-child-no-prop-drilling\">Stage 3: \u003Ccode>useFormStatus\u003C\u002Fcode> in a Child, No Prop Drilling\u003C\u002Fa>","\u003Ca href=\"#stage-4-useoptimistic-for-instant-feedback\">Stage 4: \u003Ccode>useOptimistic\u003C\u002Fcode> for Instant Feedback\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge Cases and Gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices-when-not-to-reach-for-actions\">Best Practices: When (Not) to Reach for Actions\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat Sheet\u003C\u002Fa>","\u003Ca href=\"#key-takeaways\">Key Takeaways\u003C\u002Fa>",{"id":131,"html":132,"text":132,"type":94,"level":31},"b11","The Problem: A Pending State You Track By Hand",{"id":134,"html":135,"type":83},"b12","\u003Cp>Here&#39;s the version most of us wrote before React 19:\u003C\u002Fp>",{"id":137,"code":138,"type":139,"language":140,"highlight":141},"b13","function ProfileForm() {\n  const [name, setName] = useState(\"\");\n  const [isSubmitting, setIsSubmitting] = useState(false);\n  const [error, setError] = useState(null);\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    setIsSubmitting(true);\n    setError(null);\n    try {\n      await saveProfile(name);\n    } catch (err) {\n      setError(err.message);\n    } finally {\n      setIsSubmitting(false); \u002F\u002F easy to forget, easy to skip\n    }\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput value={name} onChange={(e) => setName(e.target.value)} \u002F>\n      \u003Cbutton disabled={isSubmitting}>{isSubmitting ? \"Saving…\" : \"Save\"}\u003C\u002Fbutton>\n      {error && \u003Cp role=\"alert\">{error}\u003C\u002Fp>}\n    \u003C\u002Fform>\n  );\n}","code","jsx",[],{"id":143,"html":144,"type":83},"b14","\u003Cp>This isn&#39;t wrong, and for a single form it isn&#39;t even that risky. The bugs show up as the form grows: a second early \u003Ccode>return\u003C\u002Fcode> added months later that bypasses the \u003Ccode>try\u003C\u002Fcode>, a child button that also needs to know \u003Ccode>isSubmitting\u003C\u002Fcode> and now takes it as a prop, a second submit handler that copies this pattern and forgets the \u003Ccode>finally\u003C\u002Fcode>. \u003Ccode>isSubmitting\u003C\u002Fcode> is a fact you&#39;re asserting about the world — &quot;a request is in flight&quot; — and nothing enforces that the assertion stays true. It&#39;s exactly the kind of derived state that&#39;s easy to let rot, the same category of bug as storing a value in state when you could have computed it from something else.\u003C\u002Fp>",{"id":146,"html":147,"text":147,"type":94,"level":31},"b15","The Mental Model: Pending Is Derived, Not Stored",{"id":149,"html":150,"type":83},"b16","\u003Cp>\u003Cstrong>The mental model:\u003C\u002Fstrong> a function passed to a \u003Ccode>&lt;form&gt;\u003C\u002Fcode>&#39;s \u003Ccode>action\u003C\u002Fcode> prop (or a \u003Ccode>&lt;button&gt;\u003C\u002Fcode>&#39;s \u003Ccode>formAction\u003C\u002Fcode> prop) becomes an \u003Cstrong>Action\u003C\u002Fstrong> — React runs it inside an implicit transition, the same mechanism behind \u003Ccode>startTransition\u003C\u002Fcode>. The pending flag you get back (\u003Ccode>isPending\u003C\u002Fcode> from \u003Ccode>useActionState\u003C\u002Fcode>, or \u003Ccode>pending\u003C\u002Fcode> from \u003Ccode>useFormStatus\u003C\u002Fcode>) is not a \u003Ccode>useState\u003C\u002Fcode> you or React set by hand — it&#39;s derived from whether that transition is currently in flight. It flips \u003Ccode>true\u003C\u002Fcode> the instant the action starts and flips back \u003Ccode>false\u003C\u002Fcode> when the action&#39;s returned state finishes committing, on the success path and the error path alike, because both are just the transition ending. There&#39;s no \u003Ccode>finally\u003C\u002Fcode> to forget, because there&#39;s no manually-set flag to reset.\u003C\u002Fp>",{"id":152,"html":153,"type":83},"b17","\u003Cp>That&#39;s the whole shift: you stop asserting &quot;a request is in flight&quot; with a boolean you maintain, and start asking React &quot;is the transition I handed you still running&quot; — a question it can always answer correctly, because it&#39;s the one running it.\u003C\u002Fp>",{"id":155,"html":156,"text":157,"type":94,"level":31},"b18","Stage 1: A Plain Action on a \u003Ccode>&lt;form&gt;\u003C\u002Fcode>","Stage 1: A Plain Action on a \u003Cform>",{"id":159,"html":160,"type":83},"b19","\u003Cp>The smallest form of this is passing a function directly to \u003Ccode>action\u003C\u002Fcode> — no hook required yet:\u003C\u002Fp>",{"id":162,"code":163,"type":139,"language":140,"highlight":164},"b20","function NewsletterForm() {\n  async function subscribe(formData) {\n    const email = formData.get(\"email\");\n    await api.subscribe(email);\n  }\n\n  return (\n    \u003Cform action={subscribe}>\n      \u003Cinput name=\"email\" type=\"email\" required \u002F>\n      \u003Cbutton type=\"submit\">Subscribe\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  );\n}",[],{"id":166,"html":167,"type":83},"b21","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> the function receives the submitted \u003Ccode>FormData\u003C\u002Fcode> directly — no \u003Ccode>e.preventDefault()\u003C\u002Fcode>, no reading \u003Ccode>e.target.elements\u003C\u002Fcode> by hand. React intercepts the native submit, builds the \u003Ccode>FormData\u003C\u002Fcode>, and calls your function inside a transition. On success, React automatically resets the form&#39;s \u003Cstrong>uncontrolled\u003C\u002Fstrong> fields, mirroring what a plain HTML form submission would have done. This stage has no visible pending state yet — for that, reach for \u003Ccode>useActionState\u003C\u002Fcode>.\u003C\u002Fp>",{"id":169,"html":170,"text":171,"type":94,"level":31},"b22","Stage 2: \u003Ccode>useActionState\u003C\u002Fcode> for Result + Pending","Stage 2: useActionState for Result + Pending",{"id":173,"html":174,"type":83},"b23","\u003Cp>\u003Ccode>useActionState\u003C\u002Fcode> bundles three things a form usually needs — the action&#39;s result, whether it&#39;s still running, and a wrapped version of the action to pass to \u003Ccode>action\u003C\u002Fcode> — into one hook:\u003C\u002Fp>",{"id":176,"code":177,"type":139,"language":140,"highlight":178},"b24","import { useActionState } from \"react\";\n\nasync function saveName(previousState, formData) {\n  const name = formData.get(\"name\");\n  if (!name.trim()) {\n    return { error: \"Name can't be empty\" }; \u002F\u002F returned, not thrown\n  }\n  await api.saveName(name);\n  return { error: null };\n}\n\nfunction ProfileForm() {\n  const [state, formAction, isPending] = useActionState(saveName, { error: null });\n\n  return (\n    \u003Cform action={formAction}>\n      \u003Cinput name=\"name\" \u002F>\n      \u003Cbutton disabled={isPending}>{isPending ? \"Saving…\" : \"Save\"}\u003C\u002Fbutton>\n      {state.error && \u003Cp role=\"alert\">{state.error}\u003C\u002Fp>}\n    \u003C\u002Fform>\n  );\n}",[],{"id":180,"html":181,"type":83},"b25","\u003Cp>The signature is \u003Ccode>useActionState(fn, initialState, permalink?)\u003C\u002Fcode>, returning \u003Ccode>[state, formAction, isPending]\u003C\u002Fcode>. \u003Ccode>fn\u003C\u002Fcode> receives \u003Ccode>(previousState, formData)\u003C\u002Fcode> — the previous return value is threaded back in automatically, which is exactly the &quot;give me the last result&quot; pattern a reducer gives you, applied to an async action. \u003Ccode>state\u003C\u002Fcode> is \u003Ccode>initialState\u003C\u002Fcode> until the action has run once, then it&#39;s whatever \u003Ccode>fn\u003C\u002Fcode> last returned. \u003Ccode>formAction\u003C\u002Fcode> is what you pass to the form&#39;s \u003Ccode>action\u003C\u002Fcode> prop; calling the raw \u003Ccode>saveName\u003C\u002Fcode> directly wouldn&#39;t give you \u003Ccode>isPending\u003C\u002Fcode> or the threaded \u003Ccode>previousState\u003C\u002Fcode>.\u003C\u002Fp>",{"id":183,"html":184,"type":83},"b26","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> the error here is \u003Cem>returned\u003C\u002Fem>, not thrown. That distinction matters enough to get its own section below — it&#39;s the one real trap in this whole feature.\u003C\u002Fp>",{"id":186,"html":187,"text":188,"type":94,"level":31},"b27","Stage 3: \u003Ccode>useFormStatus\u003C\u002Fcode> in a Child, No Prop Drilling","Stage 3: useFormStatus in a Child, No Prop Drilling",{"id":190,"html":191,"type":83},"b28","\u003Cp>A submit button that needs to know &quot;is my parent form pending&quot; doesn&#39;t have to receive that as a prop:\u003C\u002Fp>",{"id":193,"code":194,"type":139,"language":140,"highlight":195},"b29","import { useFormStatus } from \"react-dom\";\n\nfunction SubmitButton() {\n  const { pending } = useFormStatus();\n  return \u003Cbutton disabled={pending}>{pending ? \"Saving…\" : \"Save\"}\u003C\u002Fbutton>;\n}\n\nfunction ProfileForm() {\n  const [state, formAction] = useActionState(saveName, { error: null });\n  return (\n    \u003Cform action={formAction}>\n      \u003Cinput name=\"name\" \u002F>\n      \u003CSubmitButton \u002F>\n    \u003C\u002Fform>\n  );\n}",[],{"id":197,"html":198,"type":83},"b30","\u003Cp>\u003Ccode>useFormStatus\u003C\u002Fcode> reads status from the nearest enclosing \u003Ccode>&lt;form&gt;\u003C\u002Fcode> through context — \u003Ccode>{ pending, data, method, action }\u003C\u002Fcode>. \u003Cstrong>The one rule that trips people up:\u003C\u002Fstrong> the component calling it must be a \u003Cem>descendant\u003C\u002Fem> of the \u003Ccode>&lt;form&gt;\u003C\u002Fcode>, never the same component that renders the form. Call it inside \u003Ccode>ProfileForm\u003C\u002Fcode> itself and \u003Ccode>pending\u003C\u002Fcode> is always \u003Ccode>false\u003C\u002Fcode>, because from \u003Ccode>&lt;form&gt;\u003C\u002Fcode>&#39;s own perspective there&#39;s no enclosing form to read.\u003C\u002Fp>",{"id":200,"html":201,"type":83},"b31","\u003Cp>This is the payoff for a design system: a \u003Ccode>&lt;SubmitButton&gt;\u003C\u002Fcode> component that works inside \u003Cem>any\u003C\u002Fem> form, with zero props, because the status lives in context rather than being threaded down by hand.\u003C\u002Fp>",{"id":203,"html":204,"text":205,"type":94,"level":31},"b32","Stage 4: \u003Ccode>useOptimistic\u003C\u002Fcode> for Instant Feedback","Stage 4: useOptimistic for Instant Feedback",{"id":207,"html":208,"type":83},"b33","\u003Cp>Layered on top, \u003Ccode>useOptimistic(actualState, updateFn?)\u003C\u002Fcode> returns \u003Ccode>[optimisticState, addOptimistic]\u003C\u002Fcode> — a value that snaps back to \u003Ccode>actualState\u003C\u002Fcode> once the surrounding action settles, letting the UI update the instant the user acts rather than waiting for the network:\u003C\u002Fp>",{"id":210,"code":211,"type":139,"language":140,"highlight":212},"b34","function LikeButton({ postId, likedByMe, likeCount }) {\n  const [optimisticLiked, setOptimisticLiked] = useOptimistic(likedByMe);\n\n  async function toggleLike() {\n    setOptimisticLiked(!optimisticLiked); \u002F\u002F must run inside a transition\n    await api.toggleLike(postId);\n  }\n\n  return (\n    \u003Cbutton onClick={() => startTransition(toggleLike)}>\n      {optimisticLiked ? \"♥ Liked\" : \"♡ Like\"} ({likeCount})\n    \u003C\u002Fbutton>\n  );\n}",[],{"id":214,"html":215,"type":83},"b35","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>setOptimisticLiked\u003C\u002Fcode> only has an effect while called inside a transition (an Action, or an explicit \u003Ccode>startTransition\u003C\u002Fcode>) — outside one, it&#39;s a no-op that just re-renders with the real state. If \u003Ccode>toggleLike\u003C\u002Fcode> fails and never updates the real \u003Ccode>likedByMe\u003C\u002Fcode>, the optimistic value reverts on its own once the transition ends; you don&#39;t manually roll it back the way you would with a hand-rolled optimistic update.\u003C\u002Fp>",{"id":217,"html":218,"type":83},"b36","\u003C!-- playground:start -->",{"id":220,"html":221,"text":221,"type":94,"level":31},"b37","🎮 Try it yourself",{"id":223,"html":224,"type":83},"b38","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-form-actions-pending-state\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":226,"html":227,"type":83},"b39","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":229,"html":230,"type":83},"b40","\u003C!-- playground:end -->",{"id":232,"html":233,"text":233,"type":94,"level":31},"b41","Edge Cases and Gotchas",{"id":235,"type":100,"items":236,"ordered":18},"b42",[237,238,239,240,241],"\u003Cstrong>Throwing vs. returning an error is not a style choice.\u003C\u002Fstrong> If your action function throws instead of returning an error value, React treats that like any other render-phase error inside a transition: it propagates to the nearest error boundary and unmounts the subtree, \u003Ccode>isPending\u003C\u002Fcode> included. \u003Ccode>saveName\u003C\u002Fcode> in Stage 2 returns \u003Ccode>{ error }\u003C\u002Fcode> precisely to avoid this — catch inside the action, and hand the failure back as state.","\u003Cstrong>Auto-reset only touches uncontrolled fields.\u003C\u002Fstrong> After a successful Action, React resets uncontrolled inputs (no \u003Ccode>value\u003C\u002Fcode>\u002F\u003Ccode>onChange\u003C\u002Fcode>) the way a native form submission would. Controlled inputs are yours to manage — they won&#39;t be touched, so a controlled field&#39;s value persists unless you clear it yourself. If you need to force a reset (including of controlled state), \u003Ccode>react-dom\u003C\u002Fcode>&#39;s \u003Ccode>requestFormReset(formElement)\u003C\u002Fcode> is the escape hatch.","\u003Cstrong>\u003Ccode>useFormStatus\u003C\u002Fcode> walks the component tree, not the DOM.\u003C\u002Fstrong> It resolves the nearest \u003Ccode>&lt;form&gt;\u003C\u002Fcode> your component is rendered under in JSX, not whichever form the DOM engine would associate a button with. A button rendered outside the \u003Ccode>&lt;form&gt;\u003C\u002Fcode> element via a portal, for instance, won&#39;t see that form&#39;s status.","\u003Cstrong>A pending Action doesn&#39;t pause the rest of the UI.\u003C\u002Fstrong> Because it runs in a transition, other state updates and navigation stay responsive while it&#39;s in flight — this is React&#39;s general &quot;keep the UI interactive&quot; behavior for transitions, not something specific to forms.","\u003Cstrong>This is client-side pending state, not automatic request deduplication.\u003C\u002Fstrong> \u003Ccode>isPending\u003C\u002Fcode> correctly reflects whether the transition is running; it doesn&#39;t by itself stop a form from being submitted a second time before your UI re-renders with the disabled button. Disabling the trigger while \u003Ccode>pending\u003C\u002Fcode>\u002F\u003Ccode>isPending\u003C\u002Fcode> is \u003Ccode>true\u003C\u002Fcode>, as every example above does, is still your job — Actions just guarantee that flag is trustworthy.",{"id":243,"html":244,"text":244,"type":94,"level":31},"b43","Best Practices: When (Not) to Reach for Actions",{"id":246,"type":100,"items":247,"ordered":18},"b44",[248,249,250,251,252],"\u003Cstrong>Reach for \u003Ccode>useActionState\u003C\u002Fcode>\u003C\u002Fstrong> whenever a form has a result to show (success message, validation error, saved data) and a pending state to disable during. It&#39;s a strict improvement over hand-rolled \u003Ccode>useState\u003C\u002Fcode> + \u003Ccode>try\u002Fcatch\u002Ffinally\u003C\u002Fcode> for that exact shape.","\u003Cstrong>Reach for \u003Ccode>useFormStatus\u003C\u002Fcode>\u003C\u002Fstrong> the moment a submit control needs pending status and isn&#39;t the component that owns the form — a shared \u003Ccode>&lt;SubmitButton&gt;\u003C\u002Fcode>, a spinner nested a level down, a disabled fieldset.","\u003Cstrong>Reach for \u003Ccode>useOptimistic\u003C\u002Fcode>\u003C\u002Fstrong> when the action is very likely to succeed and the cost of being visibly wrong for a second is lower than the cost of a spinner — likes, votes, toggles. Skip it for anything where showing the wrong state, even briefly, would mislead the user (payments, destructive actions).","\u003Cstrong>Don&#39;t reach for any of this\u003C\u002Fstrong> for state that has nothing to do with a pending async operation — a controlled input&#39;s live value, a modal&#39;s open\u002Fclosed flag, a filter. Actions solve the async-pending problem specifically; they aren&#39;t a general replacement for \u003Ccode>useState\u003C\u002Fcode>.","\u003Cstrong>Always return errors from the action\u003C\u002Fstrong>, never throw across the transition boundary, unless you deliberately want the nearest error boundary to catch it.",{"id":254,"html":255,"text":255,"type":94,"level":31},"b45","FAQ",{"id":257,"html":258,"text":259,"type":94,"level":43},"b46","Do I need a server or Next.js to use \u003Ccode>useActionState\u003C\u002Fcode>?","Do I need a server or Next.js to use useActionState?",{"id":261,"html":262,"type":83},"b47","\u003Cp>No. Everything in this article runs with plain client-side functions — \u003Ccode>saveName\u003C\u002Fcode>, \u003Ccode>subscribe\u003C\u002Fcode>, \u003Ccode>toggleLike\u003C\u002Fcode> are ordinary async functions, no \u003Ccode>&quot;use server&quot;\u003C\u002Fcode> directive involved. Server Actions (functions that actually execute on a server, callable from a client component) are a related but separate feature that frameworks like Next.js build on top of this same Actions mechanism.\u003C\u002Fp>",{"id":264,"html":265,"text":265,"type":94,"level":43},"b48","Does the form really reset itself after I submit?",{"id":267,"html":268,"type":83},"b49","\u003Cp>Only its uncontrolled fields, and only after the action&#39;s promise resolves without the action itself preventing it. Controlled fields (anything with \u003Ccode>value\u003C\u002Fcode> tied to state) are unaffected — you&#39;re already the one deciding what they show.\u003C\u002Fp>",{"id":270,"html":271,"text":272,"type":94,"level":43},"b50","What&#39;s the difference between \u003Ccode>useActionState\u003C\u002Fcode>&#39;s \u003Ccode>isPending\u003C\u002Fcode> and \u003Ccode>useFormStatus\u003C\u002Fcode>&#39;s \u003Ccode>pending\u003C\u002Fcode>?","What's the difference between useActionState's isPending and useFormStatus's pending?",{"id":274,"html":275,"type":83},"b51","\u003Cp>They describe the same underlying transition. \u003Ccode>useActionState\u003C\u002Fcode>&#39;s \u003Ccode>isPending\u003C\u002Fcode> is available in the component that owns the form and called the hook. \u003Ccode>useFormStatus\u003C\u002Fcode>&#39;s \u003Ccode>pending\u003C\u002Fcode> is for a descendant component that doesn&#39;t have access to that hook&#39;s return value and would otherwise need it prop-drilled down.\u003C\u002Fp>",{"id":277,"html":278,"text":279,"type":94,"level":43},"b52","Can \u003Ccode>useOptimistic\u003C\u002Fcode> be used outside a form?","Can useOptimistic be used outside a form?",{"id":281,"html":282,"type":83},"b53","\u003Cp>Yes — nothing about it is form-specific. The like-button example above never touches a \u003Ccode>&lt;form&gt;\u003C\u002Fcode>; it just needs the state update wrapped in a transition (an Action or \u003Ccode>startTransition\u003C\u002Fcode>) to take effect.\u003C\u002Fp>",{"id":284,"html":285,"text":285,"type":94,"level":43},"b54","What happens if two Actions on the same form fire close together?",{"id":287,"html":288,"type":83},"b55","\u003Cp>Each call to the wrapped \u003Ccode>formAction\u003C\u002Fcode> starts its own transition; React processes them in the order they were dispatched, threading each \u003Ccode>previousState\u003C\u002Fcode> from the prior one. In practice, keep the trigger disabled while \u003Ccode>pending\u003C\u002Fcode>\u002F\u003Ccode>isPending\u003C\u002Fcode> is \u003Ccode>true\u003C\u002Fcode> (every example above does this) so a second dispatch normally can&#39;t start until the first finishes.\u003C\u002Fp>",{"id":290,"html":291,"text":291,"type":94,"level":31},"b56","Cheat Sheet",{"id":293,"head":294,"rows":298,"type":319},"b57",[295,296,297],"Need","API","Returns",[299,303,307,311,315],[300,301,302],"Result + pending flag for a form","\u003Ccode>useActionState(fn, initialState, permalink?)\u003C\u002Fcode>","\u003Ccode>[state, formAction, isPending]\u003C\u002Fcode>",[304,305,306],"Pending status in a child, no props","\u003Ccode>useFormStatus()\u003C\u002Fcode> (from \u003Ccode>react-dom\u003C\u002Fcode>)","\u003Ccode>{ pending, data, method, action }\u003C\u002Fcode> — caller must be a \u003Cstrong>descendant\u003C\u002Fstrong> of the \u003Ccode>&lt;form&gt;\u003C\u002Fcode>",[308,309,310],"Instant UI update tied to a transition","\u003Ccode>useOptimistic(actualState, updateFn?)\u003C\u002Fcode>","\u003Ccode>[optimisticState, addOptimistic]\u003C\u002Fcode>",[312,313,314],"Force a form reset (incl. controlled state)","\u003Ccode>requestFormReset(formElement)\u003C\u002Fcode> (from \u003Ccode>react-dom\u003C\u002Fcode>)","—",[316,317,318],"Report a submission failure","\u003Ccode>return { error }\u003C\u002Fcode> from the action","— never \u003Ccode>throw\u003C\u002Fcode>, or the nearest error boundary catches it","table",{"id":321,"code":322,"type":139,"language":140,"highlight":323},"b58","import { useActionState } from \"react\";\nimport { useFormStatus } from \"react-dom\";\n\nasync function action(prevState, formData) {\n  try {\n    await doWork(formData.get(\"field\"));\n    return { error: null };\n  } catch (err) {\n    return { error: err.message }; \u002F\u002F return, don't throw\n  }\n}\n\nfunction SubmitButton() {\n  const { pending } = useFormStatus(); \u002F\u002F must be a child of \u003Cform>\n  return \u003Cbutton disabled={pending}>{pending ? \"Working…\" : \"Submit\"}\u003C\u002Fbutton>;\n}\n\nfunction MyForm() {\n  const [state, formAction] = useActionState(action, { error: null });\n  return (\n    \u003Cform action={formAction}>\n      \u003Cinput name=\"field\" \u002F>\n      \u003CSubmitButton \u002F>\n      {state.error && \u003Cp role=\"alert\">{state.error}\u003C\u002Fp>}\n    \u003C\u002Fform>\n  );\n}",[],{"id":325,"html":326,"type":83},"b59","\u003C!-- quiz:start -->",{"id":328,"html":329,"text":329,"type":94,"level":31},"b60","🧠 Test yourself",{"id":331,"html":332,"type":83},"b61","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-form-actions-pending-state\u002Fquiz\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":334,"html":335,"type":83},"b62","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":337,"html":338,"type":83},"b63","\u003C!-- quiz:end -->",{"id":340,"html":341,"text":341,"type":94,"level":31},"b64","Key Takeaways",{"id":343,"type":100,"items":344,"ordered":18},"b65",[345,346,347,348,349],"A form Action&#39;s pending flag is derived from a transition, not a \u003Ccode>useState\u003C\u002Fcode> you maintain — it can&#39;t be left \u003Ccode>true\u003C\u002Fcode> by a missed \u003Ccode>finally\u003C\u002Fcode>, because there&#39;s no manual reset to miss.","\u003Ccode>useActionState(fn, initialState)\u003C\u002Fcode> returns \u003Ccode>[state, formAction, isPending]\u003C\u002Fcode>; \u003Ccode>fn\u003C\u002Fcode> gets \u003Ccode>(previousState, formData)\u003C\u002Fcode> and its return value becomes the next \u003Ccode>state\u003C\u002Fcode>.","\u003Ccode>useFormStatus()\u003C\u002Fcode> only sees a form&#39;s status from a descendant component — never from the component that renders the \u003Ccode>&lt;form&gt;\u003C\u002Fcode> itself.","Return errors from an action; throwing sends the error to the nearest error boundary and unmounts the pending UI instead of resetting it gracefully.","Auto-reset after a successful action only clears uncontrolled fields — controlled inputs stay exactly as your state says they should.",{"id":351,"html":352,"type":83},"b66","\u003Cp>The button that stayed disabled forever in the opening example was disabled because a human had to remember to turn it back off, and one code path forgot. Actions remove that human step for the pending flag specifically — not by hiding the async work, but by tying \u003Ccode>isPending\u003C\u002Fcode> to something that&#39;s always accurate: whether the transition you started is still running. The \u003Ccode>try\u003C\u002Fcode>\u002F\u003Ccode>catch\u003C\u002Fcode>\u002F\u003Ccode>finally\u003C\u002Fcode> scaffolding doesn&#39;t disappear; it moves inside the action, where a missed case fails loudly (a thrown error, caught by a boundary) instead of quietly (a button stuck disabled that nobody notices until support tickets show up).\u003C\u002Fp>",{"id":354,"html":355,"type":83},"b67","\u003Cp>Have you moved a form over to \u003Ccode>useActionState\u003C\u002Fcode> yet, and did the &quot;return the error, don&#39;t throw it&quot; rule bite you the first time? Tell me in the comments.\u003C\u002Fp>",{"id":357,"type":358},"b68","divider",{"id":360,"html":361,"type":83},"b69","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":363,"html":364,"type":83},"b70","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":366,"type":100,"items":367,"ordered":18},"b71",[368,369,370],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","You've built this submit button before. `useState(false)` for `isSubmitting`, set it `true` at the top of the handler, `await` the request, set it back `false` in a `finally`. It works — until a teammate adds an early `return` inside a validation branch that skips the `finally`, or a request fails in a way your `catch` didn't anticipate, and now the button is disabled forever. The state and the reality it's supposed to describe have quietly come apart.\n\nThis is episode three of **React Deep Dive**, a weekly series on what React itself decides, not JavaScript with a React import at the top. The first two episodes covered [re-render vs. remount](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-re-render-vs-remount-what-actually-triggers-each-5fok) and [what the React Compiler automates](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-compiler-10-what-usememo-you-can-delete-hgm) — useful background, neither required here. This episode is about form **Actions**: `useActionState`, `useFormStatus`, and `useOptimistic`, and the specific problem they solve, which isn't \"nicer syntax for async handlers\" — it's removing a piece of state you were maintaining by hand and could get wrong.\n\nThis article is written against **React 19.2** (verified 19.2.8, the current npm release as of this writing) and assumes React 19-era function components and hooks throughout.\n\n## What You'll Learn\n\nBy the end of this article you'll be able to:\n\n- Explain why a form Action's pending state can't drift out of sync the way a hand-written `isSubmitting` flag can\n- Use `useActionState` to bundle an action, its result, and its pending flag into one hook\n- Read pending status in a child component with `useFormStatus`, without prop-drilling a boolean\n- Layer `useOptimistic` on top for instant UI feedback while an action is in flight\n- Avoid the one real trap: throwing inside an action instead of returning an error as state\n\n## Who This Is For\n\nYou've written function components with `useState` and `useEffect`, and you've handled at least one form submission with a manual loading flag. No prior exposure to React 19's Actions is assumed.\n\n## Table of Contents\n\n- [The Problem: A Pending State You Track By Hand](#the-problem-a-pending-state-you-track-by-hand)\n- [The Mental Model: Pending Is Derived, Not Stored](#the-mental-model-pending-is-derived-not-stored)\n- [Stage 1: A Plain Action on a `\u003Cform>`](#stage-1-a-plain-action-on-a-form)\n- [Stage 2: `useActionState` for Result + Pending](#stage-2-useactionstate-for-result--pending)\n- [Stage 3: `useFormStatus` in a Child, No Prop Drilling](#stage-3-useformstatus-in-a-child-no-prop-drilling)\n- [Stage 4: `useOptimistic` for Instant Feedback](#stage-4-useoptimistic-for-instant-feedback)\n- [Edge Cases and Gotchas](#edge-cases-and-gotchas)\n- [Best Practices: When (Not) to Reach for Actions](#best-practices-when-not-to-reach-for-actions)\n- [FAQ](#faq)\n- [Cheat Sheet](#cheat-sheet)\n- [Key Takeaways](#key-takeaways)\n\n## The Problem: A Pending State You Track By Hand\n\nHere's the version most of us wrote before React 19:\n\n```jsx\nfunction ProfileForm() {\n  const [name, setName] = useState(\"\");\n  const [isSubmitting, setIsSubmitting] = useState(false);\n  const [error, setError] = useState(null);\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    setIsSubmitting(true);\n    setError(null);\n    try {\n      await saveProfile(name);\n    } catch (err) {\n      setError(err.message);\n    } finally {\n      setIsSubmitting(false); \u002F\u002F easy to forget, easy to skip\n    }\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput value={name} onChange={(e) => setName(e.target.value)} \u002F>\n      \u003Cbutton disabled={isSubmitting}>{isSubmitting ? \"Saving…\" : \"Save\"}\u003C\u002Fbutton>\n      {error && \u003Cp role=\"alert\">{error}\u003C\u002Fp>}\n    \u003C\u002Fform>\n  );\n}\n```\n\nThis isn't wrong, and for a single form it isn't even that risky. The bugs show up as the form grows: a second early `return` added months later that bypasses the `try`, a child button that also needs to know `isSubmitting` and now takes it as a prop, a second submit handler that copies this pattern and forgets the `finally`. `isSubmitting` is a fact you're asserting about the world — \"a request is in flight\" — and nothing enforces that the assertion stays true. It's exactly the kind of derived state that's easy to let rot, the same category of bug as storing a value in state when you could have computed it from something else.\n\n## The Mental Model: Pending Is Derived, Not Stored\n\n**The mental model:** a function passed to a `\u003Cform>`'s `action` prop (or a `\u003Cbutton>`'s `formAction` prop) becomes an **Action** — React runs it inside an implicit transition, the same mechanism behind `startTransition`. The pending flag you get back (`isPending` from `useActionState`, or `pending` from `useFormStatus`) is not a `useState` you or React set by hand — it's derived from whether that transition is currently in flight. It flips `true` the instant the action starts and flips back `false` when the action's returned state finishes committing, on the success path and the error path alike, because both are just the transition ending. There's no `finally` to forget, because there's no manually-set flag to reset.\n\nThat's the whole shift: you stop asserting \"a request is in flight\" with a boolean you maintain, and start asking React \"is the transition I handed you still running\" — a question it can always answer correctly, because it's the one running it.\n\n## Stage 1: A Plain Action on a `\u003Cform>`\n\nThe smallest form of this is passing a function directly to `action` — no hook required yet:\n\n```jsx\nfunction NewsletterForm() {\n  async function subscribe(formData) {\n    const email = formData.get(\"email\");\n    await api.subscribe(email);\n  }\n\n  return (\n    \u003Cform action={subscribe}>\n      \u003Cinput name=\"email\" type=\"email\" required \u002F>\n      \u003Cbutton type=\"submit\">Subscribe\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  );\n}\n```\n\n**Key concept:** the function receives the submitted `FormData` directly — no `e.preventDefault()`, no reading `e.target.elements` by hand. React intercepts the native submit, builds the `FormData`, and calls your function inside a transition. On success, React automatically resets the form's **uncontrolled** fields, mirroring what a plain HTML form submission would have done. This stage has no visible pending state yet — for that, reach for `useActionState`.\n\n## Stage 2: `useActionState` for Result + Pending\n\n`useActionState` bundles three things a form usually needs — the action's result, whether it's still running, and a wrapped version of the action to pass to `action` — into one hook:\n\n```jsx\nimport { useActionState } from \"react\";\n\nasync function saveName(previousState, formData) {\n  const name = formData.get(\"name\");\n  if (!name.trim()) {\n    return { error: \"Name can't be empty\" }; \u002F\u002F returned, not thrown\n  }\n  await api.saveName(name);\n  return { error: null };\n}\n\nfunction ProfileForm() {\n  const [state, formAction, isPending] = useActionState(saveName, { error: null });\n\n  return (\n    \u003Cform action={formAction}>\n      \u003Cinput name=\"name\" \u002F>\n      \u003Cbutton disabled={isPending}>{isPending ? \"Saving…\" : \"Save\"}\u003C\u002Fbutton>\n      {state.error && \u003Cp role=\"alert\">{state.error}\u003C\u002Fp>}\n    \u003C\u002Fform>\n  );\n}\n```\n\nThe signature is `useActionState(fn, initialState, permalink?)`, returning `[state, formAction, isPending]`. `fn` receives `(previousState, formData)` — the previous return value is threaded back in automatically, which is exactly the \"give me the last result\" pattern a reducer gives you, applied to an async action. `state` is `initialState` until the action has run once, then it's whatever `fn` last returned. `formAction` is what you pass to the form's `action` prop; calling the raw `saveName` directly wouldn't give you `isPending` or the threaded `previousState`.\n\n**Key concept:** the error here is *returned*, not thrown. That distinction matters enough to get its own section below — it's the one real trap in this whole feature.\n\n## Stage 3: `useFormStatus` in a Child, No Prop Drilling\n\nA submit button that needs to know \"is my parent form pending\" doesn't have to receive that as a prop:\n\n```jsx\nimport { useFormStatus } from \"react-dom\";\n\nfunction SubmitButton() {\n  const { pending } = useFormStatus();\n  return \u003Cbutton disabled={pending}>{pending ? \"Saving…\" : \"Save\"}\u003C\u002Fbutton>;\n}\n\nfunction ProfileForm() {\n  const [state, formAction] = useActionState(saveName, { error: null });\n  return (\n    \u003Cform action={formAction}>\n      \u003Cinput name=\"name\" \u002F>\n      \u003CSubmitButton \u002F>\n    \u003C\u002Fform>\n  );\n}\n```\n\n`useFormStatus` reads status from the nearest enclosing `\u003Cform>` through context — `{ pending, data, method, action }`. **The one rule that trips people up:** the component calling it must be a *descendant* of the `\u003Cform>`, never the same component that renders the form. Call it inside `ProfileForm` itself and `pending` is always `false`, because from `\u003Cform>`'s own perspective there's no enclosing form to read.\n\nThis is the payoff for a design system: a `\u003CSubmitButton>` component that works inside *any* form, with zero props, because the status lives in context rather than being threaded down by hand.\n\n## Stage 4: `useOptimistic` for Instant Feedback\n\nLayered on top, `useOptimistic(actualState, updateFn?)` returns `[optimisticState, addOptimistic]` — a value that snaps back to `actualState` once the surrounding action settles, letting the UI update the instant the user acts rather than waiting for the network:\n\n```jsx\nfunction LikeButton({ postId, likedByMe, likeCount }) {\n  const [optimisticLiked, setOptimisticLiked] = useOptimistic(likedByMe);\n\n  async function toggleLike() {\n    setOptimisticLiked(!optimisticLiked); \u002F\u002F must run inside a transition\n    await api.toggleLike(postId);\n  }\n\n  return (\n    \u003Cbutton onClick={() => startTransition(toggleLike)}>\n      {optimisticLiked ? \"♥ Liked\" : \"♡ Like\"} ({likeCount})\n    \u003C\u002Fbutton>\n  );\n}\n```\n\n**Key concept:** `setOptimisticLiked` only has an effect while called inside a transition (an Action, or an explicit `startTransition`) — outside one, it's a no-op that just re-renders with the real state. If `toggleLike` fails and never updates the real `likedByMe`, the optimistic value reverts on its own once the transition ends; you don't manually roll it back the way you would with a hand-rolled optimistic update.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-form-actions-pending-state\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## Edge Cases and Gotchas\n\n- **Throwing vs. returning an error is not a style choice.** If your action function throws instead of returning an error value, React treats that like any other render-phase error inside a transition: it propagates to the nearest error boundary and unmounts the subtree, `isPending` included. `saveName` in Stage 2 returns `{ error }` precisely to avoid this — catch inside the action, and hand the failure back as state.\n- **Auto-reset only touches uncontrolled fields.** After a successful Action, React resets uncontrolled inputs (no `value`\u002F`onChange`) the way a native form submission would. Controlled inputs are yours to manage — they won't be touched, so a controlled field's value persists unless you clear it yourself. If you need to force a reset (including of controlled state), `react-dom`'s `requestFormReset(formElement)` is the escape hatch.\n- **`useFormStatus` walks the component tree, not the DOM.** It resolves the nearest `\u003Cform>` your component is rendered under in JSX, not whichever form the DOM engine would associate a button with. A button rendered outside the `\u003Cform>` element via a portal, for instance, won't see that form's status.\n- **A pending Action doesn't pause the rest of the UI.** Because it runs in a transition, other state updates and navigation stay responsive while it's in flight — this is React's general \"keep the UI interactive\" behavior for transitions, not something specific to forms.\n- **This is client-side pending state, not automatic request deduplication.** `isPending` correctly reflects whether the transition is running; it doesn't by itself stop a form from being submitted a second time before your UI re-renders with the disabled button. Disabling the trigger while `pending`\u002F`isPending` is `true`, as every example above does, is still your job — Actions just guarantee that flag is trustworthy.\n\n## Best Practices: When (Not) to Reach for Actions\n\n- **Reach for `useActionState`** whenever a form has a result to show (success message, validation error, saved data) and a pending state to disable during. It's a strict improvement over hand-rolled `useState` + `try\u002Fcatch\u002Ffinally` for that exact shape.\n- **Reach for `useFormStatus`** the moment a submit control needs pending status and isn't the component that owns the form — a shared `\u003CSubmitButton>`, a spinner nested a level down, a disabled fieldset.\n- **Reach for `useOptimistic`** when the action is very likely to succeed and the cost of being visibly wrong for a second is lower than the cost of a spinner — likes, votes, toggles. Skip it for anything where showing the wrong state, even briefly, would mislead the user (payments, destructive actions).\n- **Don't reach for any of this** for state that has nothing to do with a pending async operation — a controlled input's live value, a modal's open\u002Fclosed flag, a filter. Actions solve the async-pending problem specifically; they aren't a general replacement for `useState`.\n- **Always return errors from the action**, never throw across the transition boundary, unless you deliberately want the nearest error boundary to catch it.\n\n## FAQ\n\n### Do I need a server or Next.js to use `useActionState`?\n\nNo. Everything in this article runs with plain client-side functions — `saveName`, `subscribe`, `toggleLike` are ordinary async functions, no `\"use server\"` directive involved. Server Actions (functions that actually execute on a server, callable from a client component) are a related but separate feature that frameworks like Next.js build on top of this same Actions mechanism.\n\n### Does the form really reset itself after I submit?\n\nOnly its uncontrolled fields, and only after the action's promise resolves without the action itself preventing it. Controlled fields (anything with `value` tied to state) are unaffected — you're already the one deciding what they show.\n\n### What's the difference between `useActionState`'s `isPending` and `useFormStatus`'s `pending`?\n\nThey describe the same underlying transition. `useActionState`'s `isPending` is available in the component that owns the form and called the hook. `useFormStatus`'s `pending` is for a descendant component that doesn't have access to that hook's return value and would otherwise need it prop-drilled down.\n\n### Can `useOptimistic` be used outside a form?\n\nYes — nothing about it is form-specific. The like-button example above never touches a `\u003Cform>`; it just needs the state update wrapped in a transition (an Action or `startTransition`) to take effect.\n\n### What happens if two Actions on the same form fire close together?\n\nEach call to the wrapped `formAction` starts its own transition; React processes them in the order they were dispatched, threading each `previousState` from the prior one. In practice, keep the trigger disabled while `pending`\u002F`isPending` is `true` (every example above does this) so a second dispatch normally can't start until the first finishes.\n\n## Cheat Sheet\n\n| Need | API | Returns |\n| --- | --- | --- |\n| Result + pending flag for a form | `useActionState(fn, initialState, permalink?)` | `[state, formAction, isPending]` |\n| Pending status in a child, no props | `useFormStatus()` (from `react-dom`) | `{ pending, data, method, action }` — caller must be a **descendant** of the `\u003Cform>` |\n| Instant UI update tied to a transition | `useOptimistic(actualState, updateFn?)` | `[optimisticState, addOptimistic]` |\n| Force a form reset (incl. controlled state) | `requestFormReset(formElement)` (from `react-dom`) | — |\n| Report a submission failure | `return { error }` from the action | — never `throw`, or the nearest error boundary catches it |\n\n```jsx\nimport { useActionState } from \"react\";\nimport { useFormStatus } from \"react-dom\";\n\nasync function action(prevState, formData) {\n  try {\n    await doWork(formData.get(\"field\"));\n    return { error: null };\n  } catch (err) {\n    return { error: err.message }; \u002F\u002F return, don't throw\n  }\n}\n\nfunction SubmitButton() {\n  const { pending } = useFormStatus(); \u002F\u002F must be a child of \u003Cform>\n  return \u003Cbutton disabled={pending}>{pending ? \"Working…\" : \"Submit\"}\u003C\u002Fbutton>;\n}\n\nfunction MyForm() {\n  const [state, formAction] = useActionState(action, { error: null });\n  return (\n    \u003Cform action={formAction}>\n      \u003Cinput name=\"field\" \u002F>\n      \u003CSubmitButton \u002F>\n      {state.error && \u003Cp role=\"alert\">{state.error}\u003C\u002Fp>}\n    \u003C\u002Fform>\n  );\n}\n```\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-form-actions-pending-state\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n## Key Takeaways\n\n- A form Action's pending flag is derived from a transition, not a `useState` you maintain — it can't be left `true` by a missed `finally`, because there's no manual reset to miss.\n- `useActionState(fn, initialState)` returns `[state, formAction, isPending]`; `fn` gets `(previousState, formData)` and its return value becomes the next `state`.\n- `useFormStatus()` only sees a form's status from a descendant component — never from the component that renders the `\u003Cform>` itself.\n- Return errors from an action; throwing sends the error to the nearest error boundary and unmounts the pending UI instead of resetting it gracefully.\n- Auto-reset after a successful action only clears uncontrolled fields — controlled inputs stay exactly as your state says they should.\n\nThe button that stayed disabled forever in the opening example was disabled because a human had to remember to turn it back off, and one code path forgot. Actions remove that human step for the pending flag specifically — not by hiding the async work, but by tying `isPending` to something that's always accurate: whether the transition you started is still running. The `try`\u002F`catch`\u002F`finally` scaffolding doesn't disappear; it moves inside the action, where a missed case fails loudly (a thrown error, caught by a boundary) instead of quietly (a button stuck disabled that nobody notices until support tickets show up).\n\nHave you moved a form over to `useActionState` yet, and did the \"return the error, don't throw it\" rule bite you the first time? Tell me in the comments.\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":373,"description":49},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-form-actions-pending-state","01a08734-5c8e-7339-b43f-b1dc2c4ab256",{"name":376,"part":43,"total":43,"items":377},"React Deep Dive",[378,382,386],{"slug":379,"title":380,"publishedAt":381,"readingMinutes":51},"react-weekly-rerender-vs-remount","React Re-render vs Remount: What Actually Triggers Each","2026-08-29T12:32:41.200Z",{"slug":383,"title":384,"publishedAt":385,"readingMinutes":51},"react-weekly-compiler-memoization","React Compiler 1.0: What useMemo You Can Delete","2026-09-05T10:17:49.849Z",{"slug":46,"title":47,"publishedAt":52,"readingMinutes":51},{"id":388,"locked":18},"01a08734-5cc1-726b-8878-3e20ad0e73ec",[390],{"id":391,"slug":46,"title":392,"_count":393},"01a08734-5cdd-743d-adec-d6ba65e75fb4","Test Yourself — React Form Actions & Pending State",{"questions":394},7,[396],{"locale":13,"slug":46},{"id":391,"slug":46,"title":392,"_count":398,"questionCount":394},{"questions":394},[400,402,404,408,412,414,418,421,423,426,430,434],{"slug":72,"name":73,"articles":401},90,{"slug":66,"name":67,"articles":403},74,{"slug":405,"name":406,"articles":407},"frontend","Frontend",69,{"slug":409,"name":410,"articles":411},"css","Css",31,{"slug":69,"name":70,"articles":413},20,{"slug":415,"name":416,"articles":417},"typescript","Typescript",12,{"slug":419,"name":420,"articles":417},"performance","Performance",{"slug":61,"name":62,"articles":422},11,{"slug":424,"name":425,"articles":394},"browser","Browser",{"slug":427,"name":428,"articles":429},"grammar","Grammar",6,{"slug":431,"name":432,"articles":433},"html","Html",5,{"slug":435,"name":436,"articles":433},"programming","Programming",{"items":438,"meta":558},[439,451,472,487,506,524,539],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":440,"likeCount":19,"commentCount":19,"author":441,"vertical":442,"topic":443,"tags":444,"_count":449,"playground":450,"hasQuiz":17,"hasPlayground":17},10,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[445,446,447,448],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":46},{"id":452,"slug":453,"title":454,"subtitle":48,"excerpt":455,"coverUrl":456,"locale":13,"readingMinutes":457,"publishedAt":458,"viewCount":459,"likeCount":19,"commentCount":19,"author":460,"vertical":461,"topic":462,"tags":465,"_count":470,"playground":471,"hasQuiz":17,"hasPlayground":17},"01a06d6c-41f6-77bd-95e3-cdd69a0f72eb","nextjs-weekly-server-actions-mutations-security","Next.js Server Actions: Mutations & Security (Cheat Sheet)","Next.js Server Actions look like plain functions but compile to public POST endpoints. Learn the mutation flow, built-in CSRF checks, and the auth you owe.","\u002Fmedia\u002Fcovers\u002Fnextjs-weekly-server-actions-mutations-security.png",15,"2026-09-08T10:57:00.975Z",143,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":463,"name":464},"nextjs","Nextjs",[466,467,468,469],{"slug":463,"name":464,"color":48},{"slug":61,"name":62,"color":48},{"slug":72,"name":73,"color":48},{"slug":69,"name":70,"color":48},{"assessments":75},{"slug":453},{"id":473,"slug":383,"title":384,"subtitle":48,"excerpt":474,"coverUrl":475,"locale":13,"readingMinutes":51,"publishedAt":385,"viewCount":476,"likeCount":19,"commentCount":19,"author":477,"vertical":478,"topic":479,"tags":480,"_count":485,"playground":486,"hasQuiz":17,"hasPlayground":17},"01a0632c-7248-73e3-9951-e8c6eaa578c7","React Compiler 1.0 is stable. Here's the re-render model it automates, which useMemo\u002FuseCallback\u002Fmemo calls you can delete, and what still needs you.","\u002Fmedia\u002Fcovers\u002Freact-weekly-compiler-memoization.png",216,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[481,482,483,484],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":383},{"id":488,"slug":489,"title":490,"subtitle":48,"excerpt":491,"coverUrl":492,"locale":13,"readingMinutes":493,"publishedAt":494,"viewCount":495,"likeCount":19,"commentCount":19,"author":496,"vertical":497,"topic":498,"tags":499,"_count":504,"playground":505,"hasQuiz":17,"hasPlayground":17},"01a0499d-b520-778f-8286-8221ec03b8b5","nextjs-weekly-cache-components-explained","Next.js Cache Components Explained (with Cheat Sheet)","How Next.js Cache Components decide what's static, what's cached, and what streams — the use cache directive, cacheLife, and Suspense explained.","\u002Fmedia\u002Fcovers\u002Fnextjs-weekly-cache-components-explained.png",14,"2026-09-01T11:24:15.087Z",221,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":463,"name":464},[500,501,502,503],{"slug":463,"name":464,"color":48},{"slug":61,"name":62,"color":48},{"slug":72,"name":73,"color":48},{"slug":69,"name":70,"color":48},{"assessments":75},{"slug":489},{"id":507,"slug":508,"title":509,"subtitle":510,"excerpt":511,"coverUrl":512,"locale":13,"readingMinutes":413,"publishedAt":513,"viewCount":459,"likeCount":19,"commentCount":19,"author":514,"vertical":515,"topic":48,"tags":516,"_count":523,"playground":48,"hasQuiz":18,"hasPlayground":18},"01a05242-45d4-724a-b453-6b7432196185","react-multi-step-form-the-complete-guide-with-cheat-sheet","React Multi-Step Form: The Complete Guide (with Cheat Sheet)","You already know how to fix that. You also know that the moment the step index stops living in `useState`, the browser back button, the per-step validation, and the focus ring all become your problem too.","Build a React multi-step form that validates per step, survives a refresh, and stays accessible — the mental model, the traps, and a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Freact-form-wizard-component-v2.png","2026-08-30T10:41:23.244Z",{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},[517,518,519,521],{"slug":405,"name":406,"color":48},{"slug":61,"name":62,"color":48},{"slug":520,"name":520,"color":48},"next",{"slug":522,"name":522,"color":48},"package",{"assessments":19},{"id":525,"slug":379,"title":380,"subtitle":48,"excerpt":526,"coverUrl":527,"locale":13,"readingMinutes":51,"publishedAt":381,"viewCount":528,"likeCount":19,"commentCount":19,"author":529,"vertical":530,"topic":531,"tags":532,"_count":537,"playground":538,"hasQuiz":17,"hasPlayground":17},"01a04446-373a-775b-8aaf-b72c8952123d","A practical guide to React re-render vs remount: what type, position, and key decide, why state resets unexpectedly, and how to force a remount.","\u002Fmedia\u002Fcovers\u002Freact-weekly-rerender-vs-remount.png",268,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[533,534,535,536],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":379},{"id":540,"slug":541,"title":542,"subtitle":48,"excerpt":543,"coverUrl":544,"locale":13,"readingMinutes":433,"publishedAt":545,"viewCount":546,"likeCount":19,"commentCount":19,"author":547,"vertical":548,"topic":549,"tags":551,"_count":556,"playground":557,"hasQuiz":17,"hasPlayground":17},"019fe660-32b6-757d-a975-3860df795f67","immutable-array-methods","Your `.sort()` is mutating state. JavaScript finally gave you the fix.","The classic array methods — sort, reverse, splice — mutate the original array, silently breaking React state updates and surprising anyone who forgot to spread first. ES2023 added four immutable counterparts: toSorted, toReversed, toSpliced, and with.","\u002Fmedia\u002Fcovers\u002Fimmutable-array-methods.png","2026-07-15T08:22:28.270Z",200,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":66,"name":550},"JavaScript",[552,553,554,555],{"slug":66,"name":67,"color":48},{"slug":72,"name":73,"color":48},{"slug":405,"name":406,"color":48},{"slug":61,"name":62,"color":48},{"assessments":75},{"slug":541},{"page":75,"perPage":394,"total":422,"totalPages":31},"\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003Cdiv>23\u003C\u002Fdiv>\u003Cdiv>24\u003C\u002Fdiv>\u003Cdiv>25\u003C\u002Fdiv>\u003Cdiv>26\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ProfileForm\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>name\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setName\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>isSubmitting\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setIsSubmitting\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>error\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setError\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">null\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">handleSubmit\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    e\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">preventDefault\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-func\">setIsSubmitting\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bool\">true\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-func\">setError\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">null\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-kwd\">try\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-func\">saveProfile\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>name\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">catch\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      \u003Cspan class=\"shj-func\">setError\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>message\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">finally\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      \u003Cspan class=\"shj-func\">setIsSubmitting\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F easy to forget, easy to skip\n\u003C\u002Fspan>    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan> \u003Cspan class=\"shj-class\">onSubmit\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>handleSubmit\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">input\u003C\u002Fspan> \u003Cspan class=\"shj-class\">value\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>name\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> \u003Cspan class=\"shj-class\">onChange\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">setName\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>target\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>value\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">disabled\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>isSubmitting\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>isSubmitting \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Saving…\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Save\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>error \u003Cspan class=\"shj-oper\">&#38;&#38;\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan> \u003Cspan class=\"shj-class\">role\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"alert\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>error\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">NewsletterForm\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">subscribe\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>formData\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> email \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> formData\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"email\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> api\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">subscribe\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>email\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan> \u003Cspan class=\"shj-class\">action\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>subscribe\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">input\u003C\u002Fspan> \u003Cspan class=\"shj-class\">name\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"email\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">type\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"email\"\u003C\u002Fspan> \u003Cspan class=\"shj-class\">required\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">type\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"submit\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>Subscribe\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> useActionState \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">saveName\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>previousState\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> formData\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> name \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> formData\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"name\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">!\u003C\u002Fspan>name\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">trim\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> error\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Name can't be empty\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F returned, not thrown\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> api\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">saveName\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>name\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> error\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ProfileForm\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>state\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> formAction\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> isPending\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useActionState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>saveName\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> error\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan> \u003Cspan class=\"shj-class\">action\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>formAction\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">input\u003C\u002Fspan> \u003Cspan class=\"shj-class\">name\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"name\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">disabled\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>isPending\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>isPending \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Saving…\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Save\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>state\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>error \u003Cspan class=\"shj-oper\">&#38;&#38;\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan> \u003Cspan class=\"shj-class\">role\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"alert\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>state\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>error\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> useFormStatus \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react-dom\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">SubmitButton\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> pending \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useFormStatus\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">disabled\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>pending\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>pending \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Saving…\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Save\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ProfileForm\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>state\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> formAction\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useActionState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>saveName\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> error\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan> \u003Cspan class=\"shj-class\">action\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>formAction\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">input\u003C\u002Fspan> \u003Cspan class=\"shj-class\">name\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"name\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">SubmitButton\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">LikeButton\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> postId\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> likedByMe\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> likeCount \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>optimisticLiked\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setOptimisticLiked\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useOptimistic\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>likedByMe\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">toggleLike\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">setOptimisticLiked\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">!\u003C\u002Fspan>optimisticLiked\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F must run inside a transition\n\u003C\u002Fspan>    \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> api\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toggleLike\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>postId\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">onClick\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">startTransition\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>toggleLike\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>optimisticLiked \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"♥ Liked\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"♡ Like\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> (\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>likeCount\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>)\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003Cdiv>23\u003C\u002Fdiv>\u003Cdiv>24\u003C\u002Fdiv>\u003Cdiv>25\u003C\u002Fdiv>\u003Cdiv>26\u003C\u002Fdiv>\u003Cdiv>27\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> useActionState \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> useFormStatus \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react-dom\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">action\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>prevState\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> formData\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">try\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-func\">doWork\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>formData\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"field\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> error\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">catch\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>err\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> error\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> err\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>message \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F return, don't throw\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">SubmitButton\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> pending \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useFormStatus\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F must be a child of &lt;form&gt;\n\u003C\u002Fspan>  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan> \u003Cspan class=\"shj-class\">disabled\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>pending\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>pending \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Working…\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Submit\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">button\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">MyForm\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>state\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> formAction\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useActionState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>action\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> error\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan> \u003Cspan class=\"shj-class\">action\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>formAction\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">input\u003C\u002Fspan> \u003Cspan class=\"shj-class\">name\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"field\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">SubmitButton\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>state\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>error \u003Cspan class=\"shj-oper\">&#38;&#38;\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan> \u003Cspan class=\"shj-class\">role\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"alert\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>state\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>error\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">p\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">form\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":566},[]]