121 lines
5 KiB
TypeScript
121 lines
5 KiB
TypeScript
|
|
||
|
import React from 'react';
|
||
|
import { ContentPage } from '../../components/ContentPage';
|
||
|
import { InfoPanel } from '../../components/InfoPanel';
|
||
|
import { ApiSection } from '../../types';
|
||
|
|
||
|
const effectApi: ApiSection[] = [
|
||
|
{
|
||
|
title: 'Chemical.Effect',
|
||
|
description: 'Creates a side effect that runs after a batch of state changes has been processed and all dependent Computeds have been updated. Effects are used for interacting with the outside world, like updating UI, making network calls, or logging. They automatically re-run when their dependencies change.',
|
||
|
entries: [
|
||
|
{
|
||
|
signature: 'Chemical.Effect(effectFn: () => CleanUpFunction | ()): Effect',
|
||
|
description: 'Constructs a new Effect.',
|
||
|
parameters: [
|
||
|
{ name: 'effectFn', type: '() => CleanUpFunction | ()', description: 'A function that performs the side effect. Any reactive objects accessed via `:get()` inside this function become dependencies. This function can optionally return a `CleanUpFunction`.' },
|
||
|
],
|
||
|
returns: { type: 'Effect', description: 'A new Effect object.' },
|
||
|
notes: '`CleanUpFunction`: A function of type `() -> ()` that is called before the `effectFn` re-runs due to dependency changes, or when the Effect is destroyed.',
|
||
|
example: `
|
||
|
local Chemical = require(game.ReplicatedStorage.Chemical)
|
||
|
local Value = Chemical.Value
|
||
|
local Effect = Chemical.Effect
|
||
|
|
||
|
local name = Value("User")
|
||
|
local counter = Value(0)
|
||
|
|
||
|
local loggingEffect = Effect(function()
|
||
|
-- This effect depends on 'name' and 'counter'
|
||
|
local currentName = name:get()
|
||
|
local currentCount = counter:get()
|
||
|
print("Effect triggered: Name is " .. currentName .. ", Count is " .. currentCount)
|
||
|
|
||
|
-- Optional cleanup function
|
||
|
return function()
|
||
|
print("Cleaning up effect for Name: " .. currentName .. ", Count: " .. currentCount)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
name:set("Admin") -- Effect re-runs (cleanup for "User", 0 runs first)
|
||
|
counter:set(5) -- Effect re-runs (cleanup for "Admin", 0 runs first)
|
||
|
-- Note: If both set calls happen in the same frame, the effect might only run once
|
||
|
-- with ("Admin", 5), after the cleanup for ("User", 0) or ("Admin",0) depending on intermediate states.
|
||
|
`,
|
||
|
},
|
||
|
],
|
||
|
},
|
||
|
{
|
||
|
title: 'Effect Methods',
|
||
|
entries: [
|
||
|
{
|
||
|
signature: 'effect:destroy()',
|
||
|
description: 'Destroys the Effect object. If the `effectFn` returned a cleanup function, it will be called. This also removes its dependencies on other reactive objects.',
|
||
|
example: `
|
||
|
local Chemical = require(game.ReplicatedStorage.Chemical)
|
||
|
local Value = Chemical.Value
|
||
|
local Effect = Chemical.Effect
|
||
|
|
||
|
local isActive = Value(true)
|
||
|
local myEffect = Effect(function()
|
||
|
if isActive:get() then
|
||
|
print("Effect is active")
|
||
|
return function() print("Effect cleaning up: was active") end
|
||
|
else
|
||
|
print("Effect is inactive")
|
||
|
return function() print("Effect cleaning up: was inactive") end
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
isActive:set(false) -- Effect re-runs
|
||
|
myEffect:destroy() -- The last cleanup function ("Effect cleaning up: was inactive") runs.
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
signature: 'effect:clean()',
|
||
|
description: 'Explicitly runs the cleanup function returned by the last execution of `effectFn`, if one exists. This is typically managed internally by the scheduler or when the effect is destroyed, but can be called manually.',
|
||
|
example: `
|
||
|
local Chemical = require(game.ReplicatedStorage.Chemical)
|
||
|
local Value = Chemical.Value
|
||
|
local Effect = Chemical.Effect
|
||
|
|
||
|
local data = Value("initial")
|
||
|
local effectWithCleanup = Effect(function()
|
||
|
local currentData = data:get()
|
||
|
print("Processing:", currentData)
|
||
|
return function() print("Cleaning up for:", currentData) end
|
||
|
end)
|
||
|
|
||
|
data:set("updated") -- Effect re-runs, cleanup for "initial" runs.
|
||
|
effectWithCleanup:clean() -- Cleanup for "updated" runs.
|
||
|
`,
|
||
|
notes: "This method is less commonly used directly. Destruction usually handles cleanup.",
|
||
|
},
|
||
|
],
|
||
|
},
|
||
|
];
|
||
|
|
||
|
export const EffectPage: React.FC = () => {
|
||
|
return (
|
||
|
<ContentPage title="Chemical.Effect" sections={effectApi}>
|
||
|
<InfoPanel type="note" title="Execution Timing & Batching">
|
||
|
<p>
|
||
|
Effects are batched and run by the Chemical scheduler. This typically happens at the
|
||
|
end of the current frame or scheduler tick, <strong>after</strong> all direct state changes
|
||
|
(e.g., from <code>:set()</code> on a Value) and <code>Computed</code> value updates for that tick
|
||
|
have been processed.
|
||
|
</p>
|
||
|
<p className="mt-2">
|
||
|
If an Effect's dependencies change multiple times within a single frame, the Effect
|
||
|
will only run <strong>once</strong>, reacting to the <strong>most recent (final) state</strong> of its
|
||
|
dependencies for that frame.
|
||
|
</p>
|
||
|
<p className="mt-2">
|
||
|
The <code>CleanUpFunction</code> (if returned by the <code>effectFn</code>) is called before the
|
||
|
<code>effectFn</code> re-runs due to dependency changes, or when the Effect is destroyed.
|
||
|
</p>
|
||
|
</InfoPanel>
|
||
|
</ContentPage>
|
||
|
);
|
||
|
};
|