1"use strict";(self.webpackChunkrdc_website=self.webpackChunkrdc_website||[]).push([[6965],{51576(e,t,n){n.d(t,{A:()=>a});var s=n(11750),r=(n(63696),n(59004)),o=n(62540);function a({children:e,className:t,onClick:n,small:a=!1}){return(0,o.jsx)("div",{className:(0,s.A)(r.A.playgroundHeader,t,n?r.A.clickable:null,{[r.A.small]:a}),onClick:n,children:e})}},45990(e,t,n){n.d(t,{A:()=>a});var s=n(11750),r=n(59004),o=n(62540);function a({tabs:e}){return(0,o.jsx)("div",{className:r.A.tabs,role:"tablist","aria-orientation":"horizontal",children:e.map((({key:e,label:t,selected:n,onSelect:a})=>(0,o.jsx)("div",{role:"tab","aria-selected":n,tabIndex:n?0:-1,onClick:a,onFocus:a,className:(0,s.A)(r.A.tab,{[r.A.selected]:n}),children:t},e)))})}},76723(e,t,n){n.d(t,{A:()=>l});var s=n(63696),r=n(90162),o=n(14837);var a=n(62540);function l({children:e}){const t=function(){const e=(0,o.A)();return(0,s.useMemo)((()=>({...e,plain:{...e.plain,backgroundColor:"transparent"}})),[e])}();return(0,a.jsx)(r.Q,{theme:t,enableTypeScript:!0,children:e})}},20211(e,t,n){n.d(t,{A:()=>w});var s=n(70575),r=n(79194),o=n(11750),a=n(63696),l=n(51576),i=n(57447),c=n(89511),d=n(19934),u=n.n(d);n(14207);var h=n(10419),p=n(70167),m=n(76922),f=n(90162),x=n(62540);const g=(0,a.memo)(f.w);function j({onChange:e,code:t}){return(0,x.jsx)(g,{onChange:e,code:t})}var v=n(95973);function C({code:e,language:t}){return(0,x.jsx)(f.w,{language:t,code:e,disabled:!0})}function b({onChange:e,code:t,path:n="",onFocus:s,tabIndex:r,highlights:o,autoFocus:l=!1,isFocused:i=!1,language:d,readOnly:f=!1}){const g=(0,a.useMemo)((()=>({...p.f,readOnly:f})),[f]),{height:b,handleMount:A}=(0,m.A)({initialContentHeight:t.split("\n").length*g.lineHeight,isFocused:i}),y=(0,a.useCallback)((e=>{if(l&&e.focus(),o){const t=function(e){if(!e.length)return[];let t=e[0],n=t;const s=[],r=()=>s.push({selectionStartLineNumber:t,selectionStartColumn:0,positionLineNumber:n,positionColumn:0});return e.forEach((e=>{e===n?n++:(r(),t=e,n=e+1)})),r(),s}(u()(o));t.length&&e.setSelections(t)}e.onDidFocusEditorText((()=>{s(r)})),A(e)}),[]),k=(0,a.useMemo)((()=>(0,x.jsx)(C,{language:d,code:t})),[d]);return(0,v.P)()?(0,x.jsx)(j,{onChange:e,code:t}):(0,x.jsx)(c.Ay,{path:n,defaultLanguage:(0,h.c)(d),onChange:e,defaultValue:t,options:g,theme:"prism",onMount:y,height:b,loading:k})}const A=(0,a.memo)(b);var y=n(59004),k=n(45990);function w({documents:e,update:t,layout:n,variant:s,interactive:r=!0,fixtureContent:o,headerControls:l}){const c=(0,i.Gs)(),d="row"===n,[u,h]=(0,a.useState)((()=>e.map((({collapsed:e})=>e)))),p=(0,a.useRef)(e.map((({col:e})=>e))).current,m=(0,a.useCallback)((e=>{h((t=>t.map(((t,n)=>p[n]?t:n!==e))))}),[p]),f=(0,a.useCallback)((e=>{h((t=>t[e]?t.map(((t,n)=>n!==e&&t)):t))}),[]),g=(0,a.useCallback)((e=>{h((t=>t.map(((t,n)=>n===e?!t:t))))}),[]),j=(0,a.useMemo)((()=>e.map(((e,n)=>e=>t(n,e??"")))),[e.length,t]);return(0,x.jsxs)("div",{className:y.A.playgroundTextEdit,children:[(0,x.jsx)(R,{fixtureContent:d?void 0:o,title:d&&1===e.length?e[0].title:void 0,controls:l}),d&&e.length>1?(0,x.jsx)(F,{documents:e,closedList:u,onClick:m,compact:"standalone"===s,hasHeaderControls:null!=l}):null,e.map(((t,n)=>(0,x.jsxs)(a.Fragment,{children:[d&&!t.col||!t.title?null:(0,x.jsx)(N,{onClick:()=>g(n),closed:u[n],title:t.title,collapsible:e.length>1||null!=o}),(0,x.jsx)(S,{hidden:u[n],interactive:r,tabIndex:n,onFocus:d&&!t.col&&e.length>1?m:f,onChange:j[n],code:t.value,path:(0,i.LH)(c,t.path),isFocused:!u[n],language:t.language,highlights:t.highlights,autoFocus:t.autoFocus})]},`${t.path}:${n}`)))]})}function S({hidden:e,interactive:t,...n}){const r=e?null:(0,x.jsx)(C,{code:n.code,language:n.language});return(0,x.jsx)("div",{className:(0,o.A)(y.A.playgroundEditor,{[y.A.hidden]:e}),children:t?(0,x.jsx)(s.A,{fallback:r,children:()=>(0,x.jsx)(A,{...n})}):r})}function N({onClick:e,closed:t,title:n,collapsible:s}){return s?(0,x.jsxs)(l.A,{small:!0,onClick:e,children:[(0,x.jsx)("span",{className:(0,o.A)(y.A.arrow,t?y.A.right:y.A.down),children:"\u25b6"}),n]}):(0,x.jsx)("div",{className:y.A.codeHeader,children:n})}function F({documents:e,closedList:t,onClick:n,compact:s,hasHeaderControls:r}){const a=e.map(((e,t)=>({document:e,index:t}))).filter((({document:e})=>!e.col));return(0,x.jsx)(l.A,{className:(0,o.A)({[y.A.subtabs]:r},y.A.noupper,y.A.tabControls),small:r||s,children:(0,x.jsx)(k.A,{tabs:a.map((({document:e,index:s})=>({key:`${e.path}:${s}`,label:e.title,selected:!t[s],onSelect:()=>n(s)})))})})}function R({title:e=(0,x.jsx)(r.A,{id:"theme.Playground.liveEditor",description:"The live editor label of the live codeblocks",children:"Editor"}),fixtureContent:t,controls:n}){return(0,x.jsxs)(x.Fragment,{children:[null!=t?(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(l.A,{small:!0,children:"Fixtures"}),t]}):null,null!=n?(0,x.jsxs)(l.A,{className:y.A.tabControls,children:[(0,x.jsx)("div",{className:y.A.title,children:e}),n]}):null]})}},97296(e,t,n){n.d(t,{A:()=>l});var s=n(94005),r=n(95973),o=n(63696);var a=n(62540);function l({app:e,repo:t="data-client",width:n="100%",height:l="500",hidedevtools:i="1",view:c="both",terminalHeight:d="0",hideNavigation:u="1",file:h,ctl:p="0",initialpath:m=""}){const f=new URLSearchParams({height:l,hidedevtools:i,view:c,terminalHeight:d,hideNavigation:u,file:h,embed:"1",ctl:p,initialpath:m}).toString(),x=e?`https://stackblitz.com/github/reactive/${t}/tree/master/examples/${e}?${f}`:`https://stackblitz.com/github/reactive/${t}/tree/master?${f}`,[g,j]=function(e={}){const{threshold:t=.1,root:n=null,rootMargin:s="0%"}=e,r=(0,o.useRef)(null),[a,l]=(0,o.useState)(!1);return(0,o.useEffect)((()=>{const e=r.current;if(!e||"function"!=typeof IntersectionObserver)return;const o=new IntersectionObserver((([e])=>{e.isIntersecting?(l(!0),o.disconnect()):l(!1)}),{threshold:t,root:n,rootMargin:s});return o.observe(e),()=>{o.disconnect()}}),[t,n,s]),[r,a]}();let v;return v=!j||r.w?(0,a.jsx)("iframe",{width:n,height:l,ref:g}):(0,a.jsx)("iframe",{src:x,width:n,height:l,ref:g,loading:"lazy",sandbox:"allow-scripts allow-same-origin"}),(0,a.jsxs)(a.Fragment,{children:[v,(0,a.jsx)("p",{style:{textAlign:"center"},children:(0,a.jsx)(s.A,{className:"button button--secondary button--sm",to:"/demos",children:"More Demos"})})]})}},26475(e,t,n){n.d(t,{A:()=>c});var s=n(11750),r=(n(63696),n(48529)),o=n(76723),a=n(20211),l=n(59004),i=n(62540);function c({children:e,row:t,defaultTab:n}){const c=(0,r.xA)(e,n),d=t??c.documents.length>1;return(0,i.jsx)(o.A,{children:(0,i.jsx)("div",{className:l.A.playgroundQueryContainer,children:(0,i.jsx)("div",{className:(0,s.A)(l.A.playgroundContainer,l.A.standaloneEditor),children:(0,i.jsx)(a.A,{...c,layout:d?"row":"stacked",variant:"standalone"})})})})}},99985(e,t,n){n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"api/useController","title":"useController() - Type safe store manipulation in React","description":"Controller provides type-safe methods to access and dispatch actions to the store.","source":"@site/../docs/core/api/useController.md","sourceDirName":"api","
1slug":"/api/useController","permalink":"/docs/api/useController","draft":false,"unlisted":false,"editUrl":"https://github.com/reactive/data-client/edit/master/docs/api/useController.md","tags":[],"version":"current","lastUpdatedBy":"Nathaniel Tucker","lastUpdatedAt":1791057677000,"frontMatter":{"title":"useController() - Type safe store manipulation in React","vue_title":"useController() - Type safe store manipulation in Vue","sidebar_label":"useController()","description":"Controller provides type-safe methods to access and dispatch actions to the store."},"sidebar":"api","previous":{"title":"useSuspense()","permalink":"/docs/api/useSuspense"},"next":{"title":"useCache()","permalink":"/docs/api/useCache"}}');var r=n(62540),o=n(43023),a=(n(26475),n(97296));const l={title:"useController() - Type safe store manipulation in React",vue_title:"useController() - Type safe store manipulation in Vue",sidebar_label:"useController()",description:"Controller provides type-safe methods to access and dispatch actions to the store."},i="useController()",c={},d=[{value:"Examples",id:"examples",level:2},{value:"Form submission",id:"form-submission",level:3},{value:"Direct entity update",id:"direct-entity-update",level:3},{value:"Invalidate after mutation",id:"invalidate-after-mutation",level:3},{value:"Prefetching",id:"prefetching",level:3},{value:"Websocket updates",id:"websocket-updates",level:3},{value:"Todo App",id:"todo-app",level:3}];function u(e){const t={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",p:"p",pre:"pre",...(0,o.R)(),...e.components},{Head:n}=t;return n||function(e,t){throw new Error("Expected "+(t?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Head",!0),(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n,{children:(0,r.jsx)("meta",{name:"docsearch:pagerank",content:"10"})}),"\n","\n",(0,r.jsx)(t.header,{children:(0,r.jsx)(t.h1,{id:"usecontroller",children:"useController()"})}),"\n",(0,r.jsxs)(t.p,{children:[(0,r.jsx)(t.a,{href:"/docs/api/Controller",children:"Controller"})," provides type-safe methods to access and dispatch actions to the store."]}),"\n",(0,r.jsxs)(t.p,{children:["For instance ",(0,r.jsx)(t.a,{href:"/docs/api/Controller#fetch",children:"fetch"}),", ",(0,r.jsx)(t.a,{href:"/docs/api/Controller#invalidate",children:"invalidate"}),",\nand ",(0,r.jsx)(t.a,{href:"/docs/api/Controller#setResponse",children:"setResponse"})]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-tsx",children:"import { useController } from '@data-client/react';\n\nfunction MyComponent({ id }) {\n const ctrl = useController();\n\n const handleRefresh = useCallback(\n async e => {\n await ctrl.fetch(MyResource.get, { id });\n },\n [fetch, id],\n );\n\n const handleSuspend = useCallback(\n async e => {\n await ctrl.invalidate(MyResource.get, { id });\n },\n [invalidate, id],\n );\n\n const handleLogout = useCallback(\n async e => {\n ctrl.resetEntireStore();\n },\n [resetEntireStore],\n );\n}\n"})}),"\n",(0,r.jsx)(t.h2,{id:"examples",children:"Examples"}),"\n",(0,r.jsx)(t.h3,{id:"form-submission",children:"Form submission"}),"\n",(0,r.jsxs)(t.p,{children:[(0,r.jsx)(t.a,{href:"/docs/api/Controller#fetch",children:"fetch"})," returns the denormalized response, matching ",(0,r.jsx)(t.a,{href:"/docs/api/useSuspense",children:"useSuspense()"}),"'s return type. This allows using Entity methods like ",(0,r.jsx)(t.code,{children:"pk()"}),"."]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-tsx",children:"function CreatePost() {\n const ctrl = useController();\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const post = await ctrl.fetch(\n PostResource.getList.push,\n new FormData(e.target as HTMLFormElement),\n );\n post.title;\n post.computedField;\n navigate(`/post/${post.pk()}`);\n };\n\n return <form onSubmit={handleSubmit}>{/* fields */}</form>;\n}\n"})}),"\n",(0,r.jsx)(t.h3,{id:"direct-entity-update",children:"Direct entity update"}),"\n",(0,r.jsxs)(t.p,{children:["Use ",(0,r.jsx)(t.a,{href:"/docs/api/Controller#set",children:"set"})," for immediate updates without network requests. Supports functional updates to avoid race conditions."]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-tsx",children:"function VoteButton({ articleId }: { articleId: string }) {\n const ctrl = useController();\n\n return (\n <button\n onClick={() =>\n ctrl.set(Article, { id: articleId }, article => ({\n ...article,\n votes: article.votes + 1,\n }))\n }\n >\n Vote\n </button>\n );\n}\n"})}),"\n",(0,r.jsx)(t.h3,{id:"invalidate-after-mutation",children:"Invalidate after mutation"}),"\n",(0,r.jsxs)(t.p,{children:["Force refetch of related data using ",(0,r.jsx)(t.a,{href:"/docs/api/Controller#invalidate",children:"invalidate"})," or ",(0,r.jsx)(t.a,{href:"/docs/api/Controller#expireAll",children:"expireAll"}),"."]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-tsx",children:"function ClearUserCache({ userId }: { userId: string }) {\n const ctrl = useController();\n\n const handleClear = async () => {\n // invalidate() causes suspense; expireAll() refetches silently\n ctrl.expireAll(UserResource.get);\n ctrl.expireAll(UserResource.getList);\n };\n\n return <button onClick={handleClear}>Refresh user data</button>;\n}\n"})}),"\n",(0,r.jsx)(t.admonition,{type:"tip",children:(0,r.jsxs)(t.p,{children:["For better performance and consistency, prefer ",(0,r.jsx)(t.a,{href:"/rest/guides/side-effects",children:"including side effect updates in mutation responses"}),"."]})}),"\n",(0,r.jsx)(t.h3,{id:"prefetching",children:"Prefetching"}),"\n",(0,r.jsxs)(t.p,{children:["Use ",(0,r.jsx)(t.a,{href:"/docs/api/Controller#fetchIfStale",children:"fetchIfStale"})," to prefetch without overfetching fresh data."]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-tsx",children:"function ArticleLink({ id }: { id: string }) {\n const ctrl = useController();\n\n return (\n <Link\n to={`/article/${id}`}\n onMouseEnter={() => ctrl.fetchIfStale(ArticleResource.get, { id })}\n >\n Read more\n </Link>\n );\n}\n"})}),"\n",(0,r.jsx)(t.h3,{id:"websocket-updates",children:"Websocket updates"}),"\n",(0,r.jsxs)(t.p,{children:["Populate cache with external data via ",(0,r.jsx)(t.a,{href:"/docs/api/Controller#set",children:"set"}),"."]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-tsx",children:"function useWebsocket(url: string) {\n const ctrl = useController();\n\n useEffect(() =>
1 {\n const ws = new WebSocket(url);\n ws.onmessage = event => {\n const { entity, args, data } = JSON.parse(event.data);\n ctrl.set(EntityMap[entity], args, data);\n };\n return () => ws.close();\n }, [ctrl, url]);\n}\n"})}),"\n",(0,r.jsx)(t.admonition,{type:"warning",children:(0,r.jsxs)(t.p,{children:["For production use, implement a ",(0,r.jsx)(t.a,{href:"/docs/concepts/managers#data-stream",children:"Manager for data streams"})," rather than component-level ","effects",". Managers handle connection lifecycle globally and work with SSR."]})}),"\n",(0,r.jsx)(t.h3,{id:"todo-app",children:"Todo App"}),"\n",(0,r.jsx)(a.A,{app:"todo-app",file:"src/resources/TodoResource.ts,src/pages/Home/TodoListItem.tsx",view:"both"})]})}function h(e={}){const{wrapper:t}={...(0,o.R)(),...e.components};return t?(0,r.jsx)(t,{...e,children:(0,r.jsx)(u,{...e})}):u(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.