Skip to main content

ObservableValue

A typed value you can read, set, and observe. Adding a listener returns a cleanup function that's safe to call more than once.

local health = ObservableValue.new(100)

local cleanup = health:onChanged(function(newHealth, oldHealth)
	print(`health went from {oldHealth} to {newHealth}`)
end)

health:set(80) --> health went from 100 to 80
health:update(function(current)
	return current - 10
end) --> health went from 80 to 70

cleanup()

Types​

Disconnect​

type Disconnect = () → ()

Every listener method returns a Disconnect, the cleanup function. Call it to stop listening. It's safe to call more than once, and can be handed to a cleanup library or returned from a React effect.

Properties​

version​

This item is read only and cannot be modified. Read Only
ObservableValue.version: number

Increases each time the value changes. It identifies a value even when a table was changed in place, which makes it a useful dependency for caches, or for React's useMemo / useEffect.

Functions​

new​

ObservableValue.new(initialValue: T) → ObservableValue<T>

Creates an ObservableValue holding initialValue.

get​

ObservableValue:get() → T

Returns the current value.

set​

ObservableValue:set(newValue: T) → ()

Sets the value. ObservableValue:onChanged fires if the value changed, and ObservableValue:onSet fires on every call.

INFO

Setting a table always fires onChanged, even if it's the same table, so a table you edited in place still notifies listeners when you set it.

update​

ObservableValue:update(update: (T) → T) → T

Sets the value to update(currentValue) and returns the new value.

health:update(function(current)
	return current - 10
end)

onChanged​

ObservableValue:onChanged(callback: (
newValue: T,
oldValue: T
) → ()) → Disconnect

Calls callback whenever the value changes.

onSet​

ObservableValue:onSet(callback: (newValue: T) → ()) → Disconnect

Calls callback on every ObservableValue:set, including ones that don't change the value.

promiseValue​

ObservableValue:promiseValue(predicate: (T) → (
boolean,
R
)) → TypedPromise<R>

Returns a promise that resolves with R the first time predicate(value) returns true, checking the current value first. If predicate errors, the promise rejects with that error. Cancelling the promise stops listening.

local loaded = ObservableValue.new(false)
loaded:promiseValue(function(isLoaded)
	return isLoaded, nil
end):expect() -- yields until loaded is true

observe​

ObservableValue:observe(callback: (
newValue: T,
oldValue: T?
) → ()) → Disconnect

Calls callback straight away with the current value (oldValue is nil), then on every change.

Destroy​

ObservableValue:Destroy() → ()

Disconnects every listener. It works as a cleanup task in Maid, Janitor and Trove.

destroy​

ObservableValue:destroy() → ()
Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Creates an ObservableValue holding `initialValue`.",
            "params": [
                {
                    "name": "initialValue",
                    "desc": "",
                    "lua_type": "T"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "ObservableValue<T>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 84,
                "path": "src/init.luau"
            }
        },
        {
            "name": "get",
            "desc": "Returns the current value.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "T"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 102,
                "path": "src/init.luau"
            }
        },
        {
            "name": "set",
            "desc": "Sets the value. [ObservableValue:onChanged] fires if the value changed, and [ObservableValue:onSet] fires on\nevery call.\n\n:::info\nSetting a table always fires `onChanged`, even if it's the same table, so a table you edited in place still\nnotifies listeners when you `set` it.\n:::",
            "params": [
                {
                    "name": "newValue",
                    "desc": "",
                    "lua_type": "T"
                }
            ],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 120,
                "path": "src/init.luau"
            }
        },
        {
            "name": "update",
            "desc": "Sets the value to `update(currentValue)` and returns the new value.\n\n```lua\nhealth:update(function(current)\n\treturn current - 10\nend)\n```",
            "params": [
                {
                    "name": "update",
                    "desc": "",
                    "lua_type": "(T) -> T"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "T"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 145,
                "path": "src/init.luau"
            }
        },
        {
            "name": "onChanged",
            "desc": "Calls `callback` whenever the value changes.",
            "params": [
                {
                    "name": "callback",
                    "desc": "",
                    "lua_type": "(newValue: T, oldValue: T) -> ()"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Disconnect"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 160,
                "path": "src/init.luau"
            }
        },
        {
            "name": "onSet",
            "desc": "Calls `callback` on every [ObservableValue:set], including ones that don't change the value.",
            "params": [
                {
                    "name": "callback",
                    "desc": "",
                    "lua_type": "(newValue: T) -> ()"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Disconnect"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 173,
                "path": "src/init.luau"
            }
        },
        {
            "name": "promiseValue",
            "desc": "Returns a promise that resolves with `R` the first time `predicate(value)` returns `true`, checking the current\nvalue first. If `predicate` errors, the promise rejects with that error. Cancelling the promise stops listening.\n\n```lua\nlocal loaded = ObservableValue.new(false)\nloaded:promiseValue(function(isLoaded)\n\treturn isLoaded, nil\nend):expect() -- yields until loaded is true\n```",
            "params": [
                {
                    "name": "predicate",
                    "desc": "",
                    "lua_type": "(T) -> (boolean, R)"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "TypedPromise<R>"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 194,
                "path": "src/init.luau"
            }
        },
        {
            "name": "observe",
            "desc": "Calls `callback` straight away with the current value (`oldValue` is `nil`), then on every change.",
            "params": [
                {
                    "name": "callback",
                    "desc": "",
                    "lua_type": "(newValue: T, oldValue: T?) -> ()"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Disconnect"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 238,
                "path": "src/init.luau"
            }
        },
        {
            "name": "Destroy",
            "desc": "Disconnects every listener. It works as a cleanup task in Maid, Janitor and Trove.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 250,
                "path": "src/init.luau"
            }
        },
        {
            "name": "destroy",
            "desc": "Alias of [ObservableValue:Destroy].",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 261,
                "path": "src/init.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "version",
            "desc": "Increases each time the value changes. It identifies a value even when a table was changed in place, which makes\nit a useful dependency for caches, or for React's `useMemo` / `useEffect`.",
            "lua_type": "number",
            "readonly": true,
            "source": {
                "line": 56,
                "path": "src/init.luau"
            }
        }
    ],
    "types": [
        {
            "name": "Disconnect",
            "desc": "Every listener method returns a `Disconnect`, the cleanup function. Call it to stop listening. It's safe to call\nmore than once, and can be handed to a cleanup library or returned from a React effect.",
            "lua_type": "() -> ()",
            "source": {
                "line": 12,
                "path": "src/init.luau"
            }
        }
    ],
    "name": "ObservableValue",
    "desc": "A typed value you can read, set, and observe. Adding a listener returns a cleanup function that's safe to call\nmore than once.\n\n```lua\nlocal health = ObservableValue.new(100)\n\nlocal cleanup = health:onChanged(function(newHealth, oldHealth)\n\tprint(`health went from {oldHealth} to {newHealth}`)\nend)\n\nhealth:set(80) --> health went from 100 to 80\nhealth:update(function(current)\n\treturn current - 10\nend) --> health went from 80 to 70\n\ncleanup()\n```",
    "source": {
        "line": 45,
        "path": "src/init.luau"
    }
}