PageSourceSearch

https://stenciljs.com/docs/assets/js/a540a31e.0fc48c99.js

js stenciljs.com collected 2026-09-24 17:51:54 UTC 14,185 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunk_stencil_stencil_site=self.webpackChunk_stencil_stencil_site||[]).push([[5147],{73878:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>h,frontMatter:()=>i,metadata:()=>o,toc:()=>l});var s=t(74848),r=t(28453);const i={title:"Internal state",sidebar_label:"Internal State",description:"Use the State() for component's internal state",slug:"/state"},a="State",o={id:"components/state",title:"Internal state",description:"Use the State() for component's internal state",source:"@site/versioned_docs/version-v4.35/components/state.md",sourceDirName:"components",slug:"/state",permalink:"/docs/v4.35/state",draft:!1,unlisted:!1,editUrl:"https://github.com/ionic-team/stencil-site/tree/main/versioned_docs/version-v4.35/components/state.md",tags:[],version:"v4.35",frontMatter:{title:"Internal state",sidebar_label:"Internal State",description:"Use the State() for component's internal state",slug:"/state"},sidebar:"docs",previous:{title:"Reactive Data",permalink:"/docs/v4.35/reactive-data"},next:{title:"Styling",permalink:"/docs/v4.35/styling"}},c={},l=[{value:"The State Decorator (<code>@State</code>)",id:"the-state-decorator-state",level:2},{value:"When to Use <code>@State()</code>?",id:"when-to-use-state",level:2},{value:"Examples",id:"examples",level:2},{value:"Using <code>@State()</code> with <code>@Listen()</code>",id:"using-state-with-listen",level:3},{value:"Complex Types",id:"complex-types",level:3}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"state",children:"State"})}),"\n",(0,s.jsx)(n.p,{children:"'State' is a general term that refers to the values and objects that are stored on a class or an instance of a class for\nuse now or in the future."}),"\n",(0,s.jsxs)(n.p,{children:["Like a regular TypeScript class, a Stencil component may have one or more internal class members for holding value(s)\nthat make up the component's state. Stencil allows developers to optionally mark class members holding some part of the\nclass's state with the ",(0,s.jsx)(n.code,{children:"@State()"})," decorator to trigger a rerender when the state changes."]}),"\n",(0,s.jsxs)(n.h2,{id:"the-state-decorator-state",children:["The State Decorator (",(0,s.jsx)(n.code,{children:"@State"}),")"]}),"\n",(0,s.jsxs)(n.p,{children:["Stencil provides a decorator to trigger a rerender when certain class members change. A component's class members that\nshould trigger a rerender must be decorated using Stencil's ",(0,s.jsx)(n.code,{children:"@State()"})," decorator, like so:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-tsx",children:"// First, we import State from '@stencil/core'\nimport { Component, State, h } from '@stencil/core';\n\n@Component({\n    tag: 'current-time',\n})\nexport class CurrentTime {\n    // Second, we decorate a class member with @State()\n    // When `currentTime` changes, a rerender will be\n    // triggered\n    @State() currentTime: number = Date.now();\n\n    render() {\n        // Within the component's class, its members are\n        // accessed via `this`. This allows us to render\n        // the value stored in `currentTime`\n        const time = new Date(this.currentTime).toLocaleTimeString();\n\n        return (\n            <span>{time}</span>\n        );\n    }\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["In the example above, ",(0,s.jsx)(n.code,{children:"@State()"})," is placed before (decorates) the ",(0,s.jsx)(n.code,{children:"currentTime"})," class member, which is a number. This\nmarks ",(0,s.jsx)(n.code,{children:"currentTime"})," so that any time its value changes, the component rerenders."]}),"\n",(0,s.jsxs)(n.p,{children:["However, the example above doesn't demonstrate the real power of using ",(0,s.jsx)(n.code,{children:"@State"}),". ",(0,s.jsx)(n.code,{children:"@State"})," members are meant to only be\nupdated within a class, which the example above never does after the initial assignment of ",(0,s.jsx)(n.code,{children:"currentTime"}),". This means\nthat our ",(0,s.jsx)(n.code,{children:"current-time"})," component will never rerender! We fix that in the example below to update ",(0,s.jsx)(n.code,{children:"current-time"})," every\n1000 milliseconds (1 second):"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-tsx",children:"import { Component, State, h } from '@stencil/core';\n\n@Component({\n    tag: 'current-time',\n})\nexport class CurrentTime {\n    timer: number;\n\n    // `currentTime` is decorated with `@State()`,\n    // as we need to trigger a rerender when its\n    // value changes to show the latest time\n    @State() currentTime: number = Date.now();\n    \n    connectedCallback() {\n        this.timer = window.setInterval(() => {            \n            // the assignment to `this.currentTime`\n            // will trigger a re-render\n            this.currentTime = Date.now();\n        }, 1000);\n    }\n\n    disconnectedCallback() {\n        window.clearInterval(this.timer);\n    }\n\n    render() {\n        const time = new Date(this.currentTime).toLocaleTimeString();
1\n\n        return (\n            <span>{time}</span>\n        );\n    }\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The example above makes use of the ",(0,s.jsx)(n.a,{href:"/docs/v4.35/component-lifecycle#connectedcallback",children:"connectedCallback() lifecycle method"}),"\nto set ",(0,s.jsx)(n.code,{children:"currentTime"})," to the value of ",(0,s.jsx)(n.code,{children:"Date.now()"})," every 1000 milliseconds (or, every one second). Because the value of\n",(0,s.jsx)(n.code,{children:"currentTime"})," changes every second, Stencil calls the ",(0,s.jsx)(n.code,{children:"render"})," function on ",(0,s.jsx)(n.code,{children:"current-time"}),", which pretty-prints the\ncurrent time."]}),"\n",(0,s.jsxs)(n.p,{children:["The example above also makes use of the\n",(0,s.jsx)(n.a,{href:"/docs/v4.35/component-lifecycle#disconnectedcallback",children:"disconnectedCallback() lifecycle method"})," to properly clean up the timer\nthat was created using ",(0,s.jsx)(n.code,{children:"setInterval"})," in ",(0,s.jsx)(n.code,{children:"connectedCallback()"}),". This isn't necessary for using ",(0,s.jsx)(n.code,{children:"@State"}),", but is a general\ngood practice when using ",(0,s.jsx)(n.code,{children:"setInterval"}),"."]}),"\n",(0,s.jsxs)(n.h2,{id:"when-to-use-state",children:["When to Use ",(0,s.jsx)(n.code,{children:"@State()"}),"?"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"@State()"})," should be used for all class members that should trigger a rerender when they change. However, not all\ninternal state might need to be decorated with ",(0,s.jsx)(n.code,{children:"@State()"}),". If you know for sure that the value will either not change or\nthat it does not need to trigger a re-rendering, ",(0,s.jsx)(n.code,{children:"@State()"})," is not necessary. It is considered a 'best practice' to\nonly use ",(0,s.jsx)(n.code,{children:"@State()"})," when absolutely necessary. Revisiting our ",(0,s.jsx)(n.code,{children:"current-time"})," component:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-tsx",children:"import { Component, State, h } from '@stencil/core';\n\n@Component({\n    tag: 'current-time',\n})\nexport class CurrentTime {\n    // `timer` is not decorated with `@State()`, as\n    // we do not wish to trigger a rerender when its\n    // value changes\n    timer: number;\n\n    // `currentTime` is decorated with `@State()`,\n    // as we need to trigger a rerender when its\n    // value changes to show the latest time\n    @State() currentTime: number = Date.now();\n\n    connectedCallback() {\n        // the assignment to `this.timer` will not\n        // trigger a re-render\n        this.timer = window.setInterval(() => {\n            // the assignment to `this.currentTime`\n            // will trigger a re-render\n            this.currentTime = Date.now();\n        }, 1000);\n    }\n\n    disconnectedCallback() {\n        window.clearInterval(this.timer);\n    }\n\n    render() {\n        const time = new Date(this.currentTime).toLocaleTimeString();\n\n        return (\n            <span>{time}</span>\n        );\n    }\n}\n"})}),"\n",(0,s.jsx)(n.h2,{id:"examples",children:"Examples"}),"\n",(0,s.jsxs)(n.h3,{id:"using-state-with-listen",children:["Using ",(0,s.jsx)(n.code,{children:"@State()"})," with ",(0,s.jsx)(n.code,{children:"@Listen()"})]}),"\n",(0,s.jsxs)(n.p,{children:["This example makes use of ",(0,s.jsx)(n.code,{children:"@State"})," and ",(0,s.jsx)(n.a,{href:"/docs/v4.35/events#listen-decorator",children:(0,s.jsx)(n.code,{children:"@Listen"})})," decorators. We define a class member\ncalled ",(0,s.jsx)(n.code,{children:"isOpen"})," and decorate it with ",(0,s.jsx)(n.code,{children:"@State()"}),". With the use of ",(0,s.jsx)(n.code,{children:"@Listen()"}),", we respond to click events toggling the\nvalue of ",(0,s.jsx)(n.code,{children:"isOpen"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-tsx",children:"import { Component, Listen, State, h } from '@stencil/core';\n\n@Component({\n  tag: 'my-toggle-button'\n})\nexport class MyToggleButton {\n    // `isOpen` is decorated with `@State()`,\n    // changes to it will trigger a rerender\n    @
1State() isOpen: boolean = true;\n\n    @Listen('click', { capture: true })\n    handleClick() {\n        // whenever a click event occurs on\n        // the component, update `isOpen`,\n        // triggering the rerender\n        this.isOpen = !this.isOpen;\n    }\n\n    render() {\n        return <button>\n          {this.isOpen ? \"Open\" : \"Closed\"}\n        </button>;\n    }\n}\n"})}),"\n",(0,s.jsx)(n.h3,{id:"complex-types",children:"Complex Types"}),"\n",(0,s.jsxs)(n.p,{children:["For more advanced use cases, ",(0,s.jsx)(n.code,{children:"@State()"})," can be used with a complex type. In the example below, we print a list of ",(0,s.jsx)(n.code,{children:"Item"}),"\nentries. Although we start with zero ",(0,s.jsx)(n.code,{children:"Item"}),"s initially, we use the same pattern as we did before to add a new ",(0,s.jsx)(n.code,{children:"Item"})," to\n",(0,s.jsx)(n.code,{children:"ItemList"}),"'s ",(0,s.jsx)(n.code,{children:"items"})," array once every 2000 milliseconds (2 seconds). Every time a new entry 
1is added to ",(0,s.jsx)(n.code,{children:"items"}),", a\nrerender occurs:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-tsx",children:"import { Component, State, h } from '@stencil/core';\n\n// a user defined, complex type describing an 'Item'\ntype Item = {\n    id: number;\n    description: string,\n}\n\n@Component({\n    tag: 'item-list',\n})\nexport class ItemList {\n    // `timer` is not decorated with `@State()`, as\n    // we do not wish to trigger a rerender when its\n    // value changes\n    timer: number;\n\n    // `items` will trigger a rerender if\n    // the value assigned to the variable changes\n    @State() items: Item[] = [];\n\n    connectedCallback() {\n        // the assignment to `this.timer` will not\n        // trigger a re-render\n        this.timer = window.setInterval(() => {\n            const newTodo: Item = {\n                description: \"Item\",\n                id: this.items.length + 1\n            };\n            // the assignment to `this.items` will\n            // trigger a re-render. the assignment\n            // using '=' is important here, as we\n            // need that to make sure the rerender\n            // occurs\n            this.items = [...this.items, newTodo];\n        }, 2000);\n    }\n\n    disconnectedCallback() {\n        window.clearInterval(this.timer);\n    }\n\n    render() {\n        return (\n            <div>\n                <h1>To-Do List</h1>\n                <ul>\n                    {this.items.map((todo) => <li>{todo.description} #{todo.id}</li>)}\n                </ul>\n            </div>\n        );\n    }\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["It's important to note that it's the reassignment of ",(0,s.jsx)(n.code,{children:"this.items"})," that is causing the rerender in ",(0,s.jsx)(n.code,{children:"connectedCallback()"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"this.items = [...this.items, newTodo];\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Mutating the existing reference to ",(0,s.jsx)(n.code,{children:"this.items"})," like in the examples below will not cause a rerender, as Stencil will\nnot know that the contents of the array has changed:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"// updating `items` either of these ways will not\n// cause a rerender\nthis.items.push(newTodo);\nthis.items[this.items.length - 1] = newTodo;\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Similar to the examples above, this code sample makes use of the\n",(0,s.jsx)(n.a,{href:"/docs/v4.35/component-lifecycle#connectedcallback",children:"connectedCallback() lifecycle method"})," to create a new ",(0,s.jsx)(n.code,{children:"Item"})," and add\nit to ",(0,s.jsx)(n.code,{children:"items"})," every 2000 milliseconds (every two seconds). The example above also makes use of the\n",(0,s.jsx)(n.a,{href:"/docs/v4.35/component-lifecycle#disconnectedcallback",children:"disconnectedCallback() lifecycle method"})," to properly clean up the timer\nthat was created using ",(0,s.jsx)(n.code,{children:"setInterval"})," in ",(0,s.jsx)(n.code,{children:"connectedCallback()"}),"."]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},28453:(e,n,t)=>{t.d(n,{R:()=>a,x:()=>o});var s=t(96540);const r={},i=s.createContext(r);function a(e){const n=s.useContext(i);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),s.createElement(i.Provider,{value:n},e.children)}}}]);

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.