131 lines
5.5 KiB
TypeScript
131 lines
5.5 KiB
TypeScript
|
|
import React from 'react';
|
|
import { ContentPage } from '../../components/ContentPage';
|
|
import { InfoPanel } from '../../components/InfoPanel';
|
|
import { ApiSection } from '../../types';
|
|
|
|
const observerApi: ApiSection[] = [
|
|
{
|
|
title: 'Chemical.Observer',
|
|
description: 'Creates an observer that reacts to changes in a stateful source (Value, Table, Map, or Computed). It allows you to register callbacks that fire when the source changes.',
|
|
entries: [
|
|
{
|
|
signature: 'Chemical.Observer<T>(sourceObject: Stateful<T>): Observer<T>',
|
|
description: 'Constructs a new Observer for the given stateful source.',
|
|
parameters: [
|
|
{ name: 'sourceObject', type: 'Stateful<T>', description: 'The Value, Table, Map, or Computed object to observe.' },
|
|
],
|
|
returns: { type: 'Observer<T>', description: 'A new Observer object.' },
|
|
example: `
|
|
local Chemical = require(game.ReplicatedStorage.Chemical)
|
|
local Value = Chemical.Value
|
|
local Map = Chemical.Map
|
|
local Observer = Chemical.Observer
|
|
|
|
local name = Value("Alice")
|
|
local nameObserver = Observer(name)
|
|
|
|
local settings = Map({ theme = "dark" })
|
|
local settingsObserver = Observer(settings)
|
|
`,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: 'Observer Methods',
|
|
entries: [
|
|
{
|
|
signature: 'observer:onChange(callback: (newValue: T, oldValue: T) -> ()): Connection',
|
|
description: 'Registers a callback function that will be invoked when the observed source value changes. The callback receives the new and old values.',
|
|
parameters: [
|
|
{ name: 'callback', type: '(newValue: T, oldValue: T) -> ()', description: 'The function to call on change.' },
|
|
],
|
|
returns: { type: 'Connection', description: 'A connection object with a `:disconnect()` method to stop listening.' },
|
|
example: `
|
|
local Chemical = require(game.ReplicatedStorage.Chemical)
|
|
local Value = Chemical.Value
|
|
local Observer = Chemical.Observer
|
|
|
|
local age = Value(25)
|
|
local ageObs = Observer(age)
|
|
|
|
local connection = ageObs:onChange(function(newAge, oldAge)
|
|
print("Age changed from " .. oldAge .. " to " .. newAge)
|
|
end)
|
|
|
|
age:set(26) -- Output: Age changed from 25 to 26 (callback fires immediately)
|
|
|
|
connection:disconnect() -- Stop observing
|
|
age:set(27) -- Callback will not fire
|
|
`,
|
|
},
|
|
{
|
|
signature: 'observer:onKVChange(callback: (path: (string|number)[], newValue: any, oldValue: any) -> ()): Connection',
|
|
description: 'If the observed source is a Table or Map, this registers a callback for fine-grained changes to its keys/values. The callback receives the path (array of keys/indices) to the changed element, the new value at that path, and the old value. This is useful for deep comparison of table structures.',
|
|
parameters: [
|
|
{ name: 'callback', type: '(path, newValue, oldValue) -> ()', description: 'The function to call on key-value change.' },
|
|
],
|
|
returns: { type: 'Connection', description: 'A connection object with a `:disconnect()` method.' },
|
|
example: `
|
|
local Chemical = require(game.ReplicatedStorage.Chemical)
|
|
local Map = Chemical.Map
|
|
local Observer = Chemical.Observer
|
|
|
|
local userProfile = Map({
|
|
name = "Bob",
|
|
details = { "email", "phone" } -- Plain table for details
|
|
})
|
|
local profileObs = Observer(userProfile)
|
|
|
|
profileObs:onKVChange(function(path, newValue, oldValue)
|
|
print("Profile changed at path:", table.concat(path, "."), "from", oldValue, "to", newValue)
|
|
end)
|
|
|
|
userProfile:key("name", "Robert")
|
|
-- Output: Profile changed at path: name from Bob to Robert (callback fires immediately)
|
|
|
|
-- To observe changes within the 'details' plain table, you'd typically
|
|
-- replace the 'details' table itself or use a Chemical.Table if 'details' needed its own reactivity.
|
|
local newDetails = table.clone(userProfile:get().details)
|
|
newDetails[1] = "new_email@example.com"
|
|
userProfile:key("details", newDetails)
|
|
-- Output: Profile changed at path: details from table: XXX to table: YYY (actual addresses will vary)
|
|
-- And then another event for the specific change if the new table is compared deeply:
|
|
-- Output: Profile changed at path: details.1 from email to new_email@example.com
|
|
`,
|
|
notes: "This method is only effective if the source object's value is a table/map and it's marked for deep comparison (Chemical handles this for Table/Map types)."
|
|
},
|
|
{
|
|
signature: 'observer:destroy()',
|
|
description: 'Destroys the Observer object, disconnecting all its registered callbacks and cleaning up resources.',
|
|
example: `
|
|
local Chemical = require(game.ReplicatedStorage.Chemical)
|
|
local Value = Chemical.Value
|
|
local Observer = Chemical.Observer
|
|
|
|
local status = Value("active")
|
|
local statusObs = Observer(status)
|
|
statusObs:onChange(function() print("Status updated") end)
|
|
|
|
-- ... later
|
|
statusObs:destroy()
|
|
status:set("inactive") -- The onChange callback will no longer fire
|
|
`,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
export const ObserverPage: React.FC = () => {
|
|
return (
|
|
<ContentPage title="Chemical.Observer" sections={observerApi}>
|
|
<InfoPanel type="note" title="Execution Timing">
|
|
<p>
|
|
Callbacks registered with an Observer (e.g., <code>observer:onChange()</code>, <code>observer:onKVChange()</code>)
|
|
are executed <strong>immediately and synchronously</strong> when the observed source's state is
|
|
updated (e.g., via <code>:set()</code> for a <code>Value</code>, or through relevant methods for <code>Table</code>/<code>Map</code>).
|
|
</p>
|
|
</InfoPanel>
|
|
</ContentPage>
|
|
);
|
|
};
|