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 (

Effects are batched and run by the Chemical scheduler. This typically happens at the end of the current frame or scheduler tick, after all direct state changes (e.g., from :set() on a Value) and Computed value updates for that tick have been processed.

If an Effect's dependencies change multiple times within a single frame, the Effect will only run once, reacting to the most recent (final) state of its dependencies for that frame.

The CleanUpFunction (if returned by the effectFn) is called before the effectFn re-runs due to dependency changes, or when the Effect is destroyed.

); };