Skip to main content

Component

Bind components to Roblox instances using the Component class and CollectionService tags.

NOTE

The Component class is made by sleitnick, this is just a modified variant of it which includes case changes (from PascalCase to camelCase), bug & edgecase fixes and performance improvements.

To avoid confusion of terms:

  • Component refers to this module.
  • Component Class (e.g. MyComponent through this documentation) refers to a class created via Component.new
  • Component Instance refers to an instance of a component class.
  • Roblox Instance refers to the Roblox instance to which the component instance is bound.

Methods and properties are tagged with the above terms to help clarify the level at which they are used.

Types

ExtensionFn

type ExtensionFn = (component) → ()

ExtensionShouldFn

type ExtensionShouldFn = (component) → boolean

Extension

interface Extension {
shouldExtendExtensionShouldFn?
shouldConstructExtensionShouldFn?
constructingExtensionFn?
constructedExtensionFn?
startingExtensionFn?
startedExtensionFn?
stoppingExtensionFn?
stoppedExtensionFn?
}

An extension allows the ability to extend the behavior of components. This is useful for adding injection systems or extending the behavior of components by wrapping around component lifecycle methods.

The shouldConstruct function can be used to indicate if the component should actually be created. This must return true or false. A component with multiple shouldConstruct extension functions must have them all return true in order for the component to be constructed. The shouldConstruct function runs before all other extension functions and component lifecycle methods.

The shouldExtend function can be used to indicate if the extension itself should be used. This can be used in order to toggle an extension on/off depending on whatever logic is appropriate. If no shouldExtend function is provided, the extension will always be used if provided as an extension to the component.

As an example, an extension could be created to simply log when the various lifecycle stages run on the component:

local Logger = {}
function Logger.constructing(component, componentClass) print("constructing", component) end
function Logger.constructed(component, componentClass) print("constructed", component) end
function Logger.starting(component, componentClass) print("starting", component) end
function Logger.started(component, componentClass) print("started", component) end
function Logger.stopping(component, componentClass) print("stopping", component) end
function Logger.stopped(component, componentClass) print("stopped", component) end

local MyComponent = Component.new({tag = "MyComponent", extensions = {Logger}})

Sometimes it is useful for an extension to control whether or not a component should be constructed. For instance, if a component on the client should only be instantiated for the local player, an extension might look like this, assuming the instance has an attribute linking it to the player's UserId:

local player = game:GetService("Players").LocalPlayer

local OnlyLocalPlayer = {}
function OnlyLocalPlayer.shouldConstruct(component)
	local ownerId = component.instance:GetAttribute("OwnerId")
	return ownerId == player.UserId
end

local MyComponent = Component.new({tag = "MyComponent", extensions = {OnlyLocalPlayer}})

It can also be useful for an extension itself to turn on/off depending on various contexts. For example, let's take the Logger from the first example, and only use that extension if the bound instance has a Log attribute set to true:

function Logger.shouldExtend(component)
	return component.instance:GetAttribute("Log") == true
end

ComponentConfig

interface ComponentConfig {
tagstring--

CollectionService tag to use

ancestors{Instance}?--

Optional array of ancestors in which components will be started

extensions{Extension}?--

Optional array of extension objects

}

Component configuration passed to Component.new.

  • If no ancestors option is included, it defaults to {workspace, game.Players}.
  • If no extensions option is included, it defaults to a blank table {}.

Properties

started

EventComponent Class
Component.started: Signal

Fired when a new instance of a component is started.

local MyComponent = Component.new({tag = "MyComponent"})

MyComponent.started:Connect(function(component) end)

stopped

EventComponent Class
Component.stopped: Signal

Fired when an instance of a component is stopped.

local MyComponent = Component.new({tag = "MyComponent"})

MyComponent.stopped:Connect(function(component) end)

instance

Component Instance
Component.instance: Instance

A reference back to the Roblox instance from within a component instance. When a component instance is created, it is bound to a specific Roblox instance, which will always be present through the Instance property.

MyComponent.started:Connect(function(component)
	local robloxInstance: Instance = component.instance
	print("Component is bound to " .. robloxInstance:GetFullName())
end)

Functions

new

Component
Component.new(configComponentConfig) → ComponentClass

Create a new custom Component class.

local MyComponent = Component.new({tag = "MyComponent"})

A full example might look like this:

local MyComponent = Component.new({
	tag = "MyComponent",
	ancestors = {workspace},
	extensions = {Logger}, -- See Logger example within the example for the Extension type
})

local AnotherComponent = require(somewhere.AnotherComponent)

-- Optional if UpdateRenderStepped should use BindToRenderStep:
MyComponent.renderPriority = Enum.renderPriority.Camera.Value

function MyComponent:construct()
	self.MyData = "Hello"
end

function MyComponent:start()
	local another = self:component(AnotherComponent)
	another:DoSomething()
end

function MyComponent:stop()
	self.MyData = "Goodbye"
end

function MyComponent:heartbeatUpdate(dt)
end

function MyComponent:steppedUpdate(time, dt)
end

function MyComponent:renderSteppedUpdate(dt)
end

fromTag

Component
Component.fromTag(tagstring) → ComponentClass?

Returns the component class bound to the given tag, if found.

fromTagPromise

Component
Component.fromTagPromise(tagstring) → Promise

Promisified version of Component.fromTag.

heartbeatUpdate

Component Class
Component.heartbeatUpdate(dtnumber) → ()

If this method is present on a component, then it will be automatically connected to RunService.Heartbeat.

TIP

Component will only ever hook up 1 heartbeat connection for every single component instance. As of writing, in sleitnick's variant, a heartbeat connection is hooked up for each component instance, which is not scalable for large scale operations.

Method

This is a method, not a function. This is a limitation of the documentation tool which should be fixed soon.

local MyComponent = Component.new({tag = "MyComponent"})

function MyComponent:heartbeatUpdate(dt)
end

steppedUpdate

Component Class
Component.steppedUpdate(
timenumber,
dtnumber
) → ()

If this method is present on a component, then it will be automatically connected to RunService.Stepped.

TIP

Component will only ever hook up 1 stepped connection for every single component instance. As of writing, in sleitnick's variant, a stepped connection is hooked up for each component instance, which is not scalable for large scale operations.

Method

This is a method, not a function. This is a limitation of the documentation tool which should be fixed soon.

local MyComponent = Component.new({tag = "MyComponent"})

function MyComponent:steppedUpdate(time, dt)
end

renderSteppedUpdate

This item only works when running on the client. ClientComponent Class
Component.renderSteppedUpdate(dtnumber) → ()

If this method is present on a component, then it will be automatically connected to RunService.RenderStepped. If the [Component].renderPriority field is found, then the component will instead use RunService:BindToRenderStep() to bind the function.

TIP

Component will only ever hook up 1 render stepped connection for every single component instance. As of writing, in sleitnick's variant, a render stepped connection is hooked up for each component instance , which is not scalable for large scale operations.

However, if renderPriority is specified, then a new render stepped connection will be hooked up for the component instance.

Method

This is a method, not a function. This is a limitation of the documentation tool which should be fixed soon.

-- Example that uses `RunService.RenderStepped` automatically:

local MyComponent = Component.new({tag = "MyComponent"})

function MyComponent:renderSteppedUpdate(dt)
end
-- Example that uses `RunService:BindToRenderStep` automatically:

local MyComponent = Component.new({tag = "MyComponent"})

-- Defining a renderPriority will force the component to use BindToRenderStep instead
MyComponent.renderPriority = Enum.renderPriority.Camera.Value

function MyComponent:renderSteppedUpdate(dt)
end

all

Component Class
Component:all() → {Component}

Gets a copy table array of all existing component objects. For example, if there was a component class linked to the "MyComponent" tag, and three Roblox instances in your game had that same tag, then calling all would return the three component instances.

local MyComponent = Component.new({tag = "MyComponent"})

-- ...

local components = MyComponent:all()
for _,component in components do
	component:DoSomethingHere()
end

fromInstance

Component Class
Component:fromInstance(instanceInstance) → Component?

Gets an instance of a component class from the given Roblox instance. Returns nil if not found.

local MyComponent = require(somewhere.MyComponent)

local myComponentInstance = MyComponent:fromInstance(workspace.SomeInstance)

fromInstancePromise

Component Class
Component:fromInstancePromise(
instanceInstance,
timeoutnumber?
) → Promise<ComponentInstance>

Resolves a promise once the component instance is present on a given Roblox instance.

An optional timeout can be provided to reject the promi se if it takes more than timeout seconds to resolve. If no timeout is supplied, timeout defaults to 60 seconds.

local MyComponent = require(somewhere.MyComponent)

MyComponent:fromInstancePromise(workspace.SomeInstance):andThen(function(myComponentInstance)
	-- Do something with the component class
end)

construct

Component Class
Component:construct() → ()

construct is called before the component is started, and should be used to construct the component instance.

WARNING

You should handle any potential data loading (or any code that needs to yield) in the construct method, as the component will not be started unless all other components bound to the same instance have been constructed. This addresses Issue 127.

A warn will be outputted in the console after about 10 seconds if there are still other pending components not yet fully constructed, while this component is awaiting to be started. You should ensure all your components construct within reasonable amount of time!

Cancellation

If this method has not yet finished when the component instance is stopped, then it will be forcefully stopped.

local MyComponent = Component.new({tag = "MyComponent"})

function MyComponent:construct()
	self.SomeData = 32
	self.OtherStuff = "HelloWorld"
end

start

Component Class
Component:start() → ()

start is called when the component is started, and all other component instances of the instance bound to the component are ready. At this point in time, it is safe to grab other components also bound to the same instance.

Cancellation

If this method has not yet finished when the component instance is stopped, then it will be forcefully stopped.

local MyComponent = Component.new({tag = "MyComponent"})
local AnotherComponent = require(somewhere.AnotherComponent)

function MyComponent:start()
	-- e.g., grab another component:
	local another = self:component(AnotherComponent)
end

stop

Component Class
Component:stop() → ()

stop is called when the component is stopped. This occurs either when the bound instance is removed from one of the whitelisted ancestors or when the matching tag is removed from the instance. This also means that the instance might be destroyed, and thus it is not safe to continue using the bound instance (e.g. self.instance) any longer.

This should be used to clean up the component.

local MyComponent = Component.new({tag = "MyComponent"})

function MyComponent:stop()
	self.SomeStuff:destroy()
end

component

Component Instance
Component:component(componentClassComponentClass) → Component?

Retrieves another component instance bound to the same Roblox instance.

local MyComponent = Component.new({tag = "MyComponent"})
local AnotherComponent = require(somewhere.AnotherComponent)

function MyComponent:start()
	local another = self:component(AnotherComponent)
end
Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Create a new custom Component class.\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n```\n\nA full example might look like this:\n\n```lua\nlocal MyComponent = Component.new({\n\ttag = \"MyComponent\",\n\tancestors = {workspace},\n\textensions = {Logger}, -- See Logger example within the example for the Extension type\n})\n\nlocal AnotherComponent = require(somewhere.AnotherComponent)\n\n-- Optional if UpdateRenderStepped should use BindToRenderStep:\nMyComponent.renderPriority = Enum.renderPriority.Camera.Value\n\nfunction MyComponent:construct()\n\tself.MyData = \"Hello\"\nend\n\nfunction MyComponent:start()\n\tlocal another = self:component(AnotherComponent)\n\tanother:DoSomething()\nend\n\nfunction MyComponent:stop()\n\tself.MyData = \"Goodbye\"\nend\n\nfunction MyComponent:heartbeatUpdate(dt)\nend\n\nfunction MyComponent:steppedUpdate(time, dt)\nend\n\nfunction MyComponent:renderSteppedUpdate(dt)\nend\n```",
            "params": [
                {
                    "name": "config",
                    "desc": "",
                    "lua_type": "ComponentConfig"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "ComponentClass"
                }
            ],
            "function_type": "static",
            "tags": [
                "Component"
            ],
            "source": {
                "line": 323,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "fromTag",
            "desc": "Returns the component class bound to the given tag, if found.",
            "params": [
                {
                    "name": "tag",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "ComponentClass?"
                }
            ],
            "function_type": "static",
            "tags": [
                "Component"
            ],
            "source": {
                "line": 349,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "fromTagPromise",
            "desc": "Promisified version of [Component.fromTag].",
            "params": [
                {
                    "name": "tag",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "static",
            "tags": [
                "Component"
            ],
            "source": {
                "line": 368,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "all",
            "desc": "Gets a copy table array of all existing component objects. For example,\nif there was a component class linked to the \"MyComponent\" tag,\nand three Roblox instances in your game had that same tag, then\ncalling `all` would return the three component instances.\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\n-- ...\n\nlocal components = MyComponent:all()\nfor _,component in components do\n\tcomponent:DoSomethingHere()\nend\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "{Component}"
                }
            ],
            "function_type": "method",
            "tags": [
                "Component Class"
            ],
            "source": {
                "line": 649,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "fromInstance",
            "desc": "Gets an instance of a component class from the given Roblox\ninstance. Returns `nil` if not found.\n\n```lua\nlocal MyComponent = require(somewhere.MyComponent)\n\nlocal myComponentInstance = MyComponent:fromInstance(workspace.SomeInstance)\n```",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Component?"
                }
            ],
            "function_type": "method",
            "tags": [
                "Component Class"
            ],
            "source": {
                "line": 666,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "fromInstancePromise",
            "desc": "Resolves a promise once the component instance is present on a given\nRoblox instance.\n\nAn optional `timeout` can be provided to reject the promi\tse if it\ntakes more than `timeout` seconds to resolve. If no timeout is\nsupplied, `timeout` defaults to 60 seconds.\n\n```lua\nlocal MyComponent = require(somewhere.MyComponent)\n\nMyComponent:fromInstancePromise(workspace.SomeInstance):andThen(function(myComponentInstance)\n\t-- Do something with the component class\nend)\n```",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                },
                {
                    "name": "timeout",
                    "desc": "",
                    "lua_type": "number?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Promise<ComponentInstance>"
                }
            ],
            "function_type": "method",
            "tags": [
                "Component Class"
            ],
            "source": {
                "line": 689,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "construct",
            "desc": "`construct` is called before the component is started, and should be used\nto construct the component instance.\n\n:::warning \nYou should handle any potential data loading (or any code that needs to yield) in the\n`construct` method, as the component **will not be started** unless all other components\nbound to the same instance have been constructed. This addresses [Issue 127](https://github.com/Sleitnick/RbxUtil/issues/127).\n\nA warn will be outputted in the console after about `10` seconds if there are still other pending components not\nyet fully constructed, while this component is awaiting to be started. You should ensure all your components construct\nwithin reasonable amount of time!\n:::\n\n:::note Cancellation\nIf this method has not yet finished when the component instance is stopped, then it will be forcefully\nstopped.\n:::\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\nfunction MyComponent:construct()\n\tself.SomeData = 32\n\tself.OtherStuff = \"HelloWorld\"\nend\n```",
            "params": [],
            "returns": [],
            "function_type": "method",
            "tags": [
                "Component Class"
            ],
            "source": {
                "line": 736,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "start",
            "desc": "`start` is called when the component is started, **and all other component instances of the instance bound to the component are ready**. At this point in time, it\nis safe to grab other components also bound to the same instance.\n\n:::note Cancellation\nIf this method has not yet finished when the component instance is stopped, then it will be forcefully\nstopped.\n:::\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\nlocal AnotherComponent = require(somewhere.AnotherComponent)\n\nfunction MyComponent:start()\n\t-- e.g., grab another component:\n\tlocal another = self:component(AnotherComponent)\nend\n```",
            "params": [],
            "returns": [],
            "function_type": "method",
            "tags": [
                "Component Class"
            ],
            "source": {
                "line": 758,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "stop",
            "desc": "`stop` is called when the component is stopped. This occurs either when the\nbound instance is removed from one of the whitelisted ancestors _or_ when\nthe matching tag is removed from the instance. This also means that the\ninstance _might_ be destroyed, and thus it is not safe to continue using\nthe bound instance (e.g. `self.instance`) any longer.\n\nThis should be used to clean up the component.\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\nfunction MyComponent:stop()\n\tself.SomeStuff:destroy()\nend\n```",
            "params": [],
            "returns": [],
            "function_type": "method",
            "tags": [
                "Component Class"
            ],
            "source": {
                "line": 778,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "component",
            "desc": "Retrieves another component instance bound to the same Roblox instance.\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\nlocal AnotherComponent = require(somewhere.AnotherComponent)\n\nfunction MyComponent:start()\n\tlocal another = self:component(AnotherComponent)\nend\n```",
            "params": [
                {
                    "name": "componentClass",
                    "desc": "",
                    "lua_type": "ComponentClass"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Component?"
                }
            ],
            "function_type": "method",
            "tags": [
                "Component Instance"
            ],
            "source": {
                "line": 796,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "heartbeatUpdate",
            "desc": "If this method is present on a component, then it will be\nautomatically connected to `RunService.Heartbeat`.\n\n:::tip \n`Component` will only ever hook up 1 heartbeat connection for **every single component instance**. As of writing,\nin sleitnick's variant, a heartbeat connection is hooked up for **each component instance**, which is not scalable for large\nscale operations.\n:::\n\n:::note Method\nThis is a method, not a function. This is a limitation\nof the documentation tool which should be fixed soon.\n:::\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\nfunction MyComponent:heartbeatUpdate(dt)\nend\n```",
            "params": [
                {
                    "name": "dt",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "Component Class"
            ],
            "source": {
                "line": 827,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "steppedUpdate",
            "desc": "If this method is present on a component, then it will be\nautomatically connected to `RunService.Stepped`.\n\n:::tip \n`Component` will only ever hook up 1 stepped connection for **every single component instance**. As of writing,\nin sleitnick's variant, a stepped connection is hooked up for **each component instance**, which is not scalable for large\nscale operations.\n:::\n\n:::note Method\nThis is a method, not a function. This is a limitation\nof the documentation tool which should be fixed soon.\n:::\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\nfunction MyComponent:steppedUpdate(time, dt)\nend\n```",
            "params": [
                {
                    "name": "time",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "dt",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "Component Class"
            ],
            "source": {
                "line": 855,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "renderSteppedUpdate",
            "desc": "If this method is present on a component, then it will be\nautomatically connected to `RunService.RenderStepped`. If\nthe `[Component].renderPriority` field is found, then the\ncomponent will instead use `RunService:BindToRenderStep()`\nto bind the function.\n\n:::tip \n`Component` will only ever hook up 1 render stepped connection for **every single component instance**. \nAs of writing, in sleitnick's variant, a render stepped connection is hooked up for **each component instance** , \nwhich is not scalable for large scale operations.\n\nHowever, if `renderPriority` is specified, then a new render stepped connection will be hooked up for the component\ninstance.\n:::\n\n:::note Method\nThis is a method, not a function. This is a limitation\nof the documentation tool which should be fixed soon.\n:::\n\n```lua\n-- Example that uses `RunService.RenderStepped` automatically:\n\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\nfunction MyComponent:renderSteppedUpdate(dt)\nend\n```\n```lua\n-- Example that uses `RunService:BindToRenderStep` automatically:\n\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\n-- Defining a renderPriority will force the component to use BindToRenderStep instead\nMyComponent.renderPriority = Enum.renderPriority.Camera.Value\n\nfunction MyComponent:renderSteppedUpdate(dt)\nend\n```",
            "params": [
                {
                    "name": "dt",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "Component Class"
            ],
            "realm": [
                "Client"
            ],
            "source": {
                "line": 902,
                "path": "src/Component/init.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "started",
            "desc": "Fired when a new instance of a component is started.\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\nMyComponent.started:Connect(function(component) end)\n```",
            "lua_type": "Signal",
            "tags": [
                "Event",
                "Component Class"
            ],
            "source": {
                "line": 138,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "stopped",
            "desc": "Fired when an instance of a component is stopped.\n\n```lua\nlocal MyComponent = Component.new({tag = \"MyComponent\"})\n\nMyComponent.stopped:Connect(function(component) end)\n```",
            "lua_type": "Signal",
            "tags": [
                "Event",
                "Component Class"
            ],
            "source": {
                "line": 153,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "instance",
            "desc": "A reference back to the _Roblox_ instance from within a _component_ instance. When\na component instance is created, it is bound to a specific Roblox instance, which\nwill always be present through the `Instance` property.\n\n```lua\nMyComponent.started:Connect(function(component)\n\tlocal robloxInstance: Instance = component.instance\n\tprint(\"Component is bound to \" .. robloxInstance:GetFullName())\nend)\n```",
            "lua_type": "Instance",
            "tags": [
                "Component Instance"
            ],
            "source": {
                "line": 170,
                "path": "src/Component/init.luau"
            }
        }
    ],
    "types": [
        {
            "name": "ExtensionFn",
            "desc": "",
            "lua_type": "(component) -> ()",
            "source": {
                "line": 12,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "ExtensionShouldFn",
            "desc": "",
            "lua_type": "(component) -> boolean",
            "source": {
                "line": 18,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "Extension",
            "desc": "An extension allows the ability to extend the behavior of\ncomponents. This is useful for adding injection systems or\nextending the behavior of components by wrapping around\ncomponent lifecycle methods.\n\nThe `shouldConstruct` function can be used to indicate\nif the component should actually be created. This must\nreturn `true` or `false`. A component with multiple\n`shouldConstruct` extension functions must have them _all_\nreturn `true` in order for the component to be constructed.\nThe `shouldConstruct` function runs _before_ all other\nextension functions and component lifecycle methods.\n\nThe `shouldExtend` function can be used to indicate if\nthe extension itself should be used. This can be used in\norder to toggle an extension on/off depending on whatever\nlogic is appropriate. If no `shouldExtend` function is\nprovided, the extension will always be used if provided\nas an extension to the component.\n\nAs an example, an extension could be created to simply log\nwhen the various lifecycle stages run on the component:\n\n```lua\nlocal Logger = {}\nfunction Logger.constructing(component, componentClass) print(\"constructing\", component) end\nfunction Logger.constructed(component, componentClass) print(\"constructed\", component) end\nfunction Logger.starting(component, componentClass) print(\"starting\", component) end\nfunction Logger.started(component, componentClass) print(\"started\", component) end\nfunction Logger.stopping(component, componentClass) print(\"stopping\", component) end\nfunction Logger.stopped(component, componentClass) print(\"stopped\", component) end\n\nlocal MyComponent = Component.new({tag = \"MyComponent\", extensions = {Logger}})\n```\n\nSometimes it is useful for an extension to control whether or\nnot a component should be constructed. For instance, if a\ncomponent on the client should only be instantiated for the\nlocal player, an extension might look like this, assuming the\ninstance has an attribute linking it to the player's UserId:\n```lua\nlocal player = game:GetService(\"Players\").LocalPlayer\n\nlocal OnlyLocalPlayer = {}\nfunction OnlyLocalPlayer.shouldConstruct(component)\n\tlocal ownerId = component.instance:GetAttribute(\"OwnerId\")\n\treturn ownerId == player.UserId\nend\n\nlocal MyComponent = Component.new({tag = \"MyComponent\", extensions = {OnlyLocalPlayer}})\n```\n\nIt can also be useful for an extension itself to turn on/off\ndepending on various contexts. For example, let's take the\nLogger from the first example, and only use that extension\nif the bound instance has a Log attribute set to `true`:\n```lua\nfunction Logger.shouldExtend(component)\n\treturn component.instance:GetAttribute(\"Log\") == true\nend\n```",
            "fields": [
                {
                    "name": "shouldExtend",
                    "lua_type": "ExtensionShouldFn?",
                    "desc": ""
                },
                {
                    "name": "shouldConstruct",
                    "lua_type": "ExtensionShouldFn?",
                    "desc": ""
                },
                {
                    "name": "constructing",
                    "lua_type": "ExtensionFn?",
                    "desc": ""
                },
                {
                    "name": "constructed",
                    "lua_type": "ExtensionFn?",
                    "desc": ""
                },
                {
                    "name": "starting",
                    "lua_type": "ExtensionFn?",
                    "desc": ""
                },
                {
                    "name": "started",
                    "lua_type": "ExtensionFn?",
                    "desc": ""
                },
                {
                    "name": "stopping",
                    "lua_type": "ExtensionFn?",
                    "desc": ""
                },
                {
                    "name": "stopped",
                    "lua_type": "ExtensionFn?",
                    "desc": ""
                }
            ],
            "source": {
                "line": 94,
                "path": "src/Component/init.luau"
            }
        },
        {
            "name": "ComponentConfig",
            "desc": "Component configuration passed to `Component.new`.\n\n- If no ancestors option is included, it defaults to `{workspace, game.Players}`.\n- If no extensions option is included, it defaults to a blank table `{}`.",
            "fields": [
                {
                    "name": "tag",
                    "lua_type": "string",
                    "desc": "CollectionService tag to use"
                },
                {
                    "name": "ancestors",
                    "lua_type": "{Instance}?",
                    "desc": "Optional array of ancestors in which components will be started"
                },
                {
                    "name": "extensions",
                    "lua_type": "{Extension}?",
                    "desc": "Optional array of extension objects"
                }
            ],
            "source": {
                "line": 117,
                "path": "src/Component/init.luau"
            }
        }
    ],
    "name": "Component",
    "desc": "Bind components to Roblox instances using the Component class and CollectionService tags.\n\n:::note\nThe Component class is made by `sleitnick`, this is just a modified variant of it which includes\ncase changes (from PascalCase to camelCase), bug & edgecase fixes and performance improvements.\n:::\n\nTo avoid confusion of terms:\n- `Component` refers to this module.\n- `Component Class` (e.g. `MyComponent` through this documentation) refers to a class created via `Component.new`\n- `Component Instance` refers to an instance of a component class.\n- `Roblox Instance` refers to the Roblox instance to which the component instance is bound.\n\nMethods and properties are tagged with the above terms to help clarify the level at which they are used.",
    "source": {
        "line": 271,
        "path": "src/Component/init.luau"
    }
}