chemical_docs/pages/api/ObserverPage.tsx

132 lines
5.5 KiB
TypeScript
Raw Normal View History

2025-06-13 16:13:43 +00:00
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>
);
};