chemical_docs/pages/CoreConceptsPage.tsx
2025-06-13 18:13:43 +02:00

254 lines
9.3 KiB
TypeScript

import React from 'react';
import { ContentPage } from '../components/ContentPage';
import { Link } from 'react-router-dom';
import { ApiSection } from '../types'; // Import ApiSection
export const CoreConceptsPage: React.FC = () => {
const intro = (
<p>
Chemical is built upon a few core concepts that work together to create reactive and manageable applications. Understanding these concepts is key to effectively using the library.
</p>
);
const sections: ApiSection[] = [
{
title: "Reactivity",
entries: [
{
signature: "Stateful Objects (Values, Tables, Maps)",
description: (
<>
At the heart of Chemical are stateful objects. These are containers for your application's data.
The primary stateful object is a <Link to="/api/value" className="text-purple-400 hover:underline">Value</Link>.
For collections, Chemical provides <Link to="/api/table" className="text-purple-400 hover:underline">Table</Link> (for arrays)
and <Link to="/api/map" className="text-purple-400 hover:underline">Map</Link> (for dictionaries).
When the data within these objects changes, Chemical's reactive system is notified.
</>
),
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Table = Chemical.Table
local Map = Chemical.Map
local name = Value("Guest")
local scores = Table({10, 20, 30})
local settings = Map({ theme = "dark", volume = 0.5 })
name:set("PlayerOne") -- Triggers reactivity
scores:insert(40) -- Triggers reactivity
settings:key("volume", 0.7) -- Triggers reactivity
`,
},
{
signature: "Computed Values",
description: (
<>
<Link to="/api/computed" className="text-purple-400 hover:underline">Computed</Link> values
are derived from one or more stateful objects. They automatically update whenever their underlying dependencies change.
This allows you to create complex data relationships without manual synchronization.
</>
),
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Computed = Chemical.Computed
local firstName = Value("John")
local lastName = Value("Doe")
local fullName = Computed(function()
return firstName:get() .. " " .. lastName:get()
end)
print(fullName:get()) -- Output: John Doe
firstName:set("Jane")
-- fullName:get() will be "Jane Doe" after scheduler runs
print("Full name after set:", fullName:get()) -- Might print old value here due to async update
`,
},
{
signature: "Effects",
description: (
<>
<Link to="/api/effect" className="text-purple-400 hover:underline">Effects</Link> are used to perform side effects in response to state changes.
This could include updating the UI, making network requests, or logging. Effects automatically re-run when their dependencies change.
They can also return a cleanup function that runs before the next execution or when the effect is destroyed.
</>
),
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Effect = Chemical.Effect
local count = Value(0)
Effect(function()
print("Current count is:", count:get())
-- Optional cleanup function
return function()
print("Cleaning up effect for count (previous value was):", count:get())
end
end)
count:set(1) -- Effect runs
count:set(2) -- Cleanup for count 1 runs, then effect for count 2 runs
`,
},
{
signature: "Observers and Watch",
description: (
<>
<Link to="/api/observer" className="text-purple-400 hover:underline">Observers</Link> provide a way to listen to changes in a specific stateful object and run callbacks.
<Link to="/api/watch" className="text-purple-400 hover:underline">Watch</Link> is a higher-level utility built on Observers for simpler change tracking.
</>
),
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Observer = Chemical.Observer
local Watch = Chemical.Watch
local name = Value("Alex")
-- Using Observer
local obs = Observer(name)
obs:onChange(function(newValue, oldValue)
print("Observer: Name changed from", oldValue, "to", newValue)
end)
-- Using Watch
local watchHandle = Watch(name, function(newValue, oldValue)
print("Watch: Name changed from", oldValue, "to", newValue)
end)
name:set("Jordan")
-- Both Observer and Watch callbacks will fire.
obs:destroy()
watchHandle:destroy()
`,
},
],
},
{
title: "UI Composition with `Compose`",
entries: [
{
signature: "Declarative UI",
description: (
<>
Chemical's <Link to="/api/compose" className="text-purple-400 hover:underline">Compose</Link> API allows you to define UI structures declaratively.
Properties of UI elements can be bound to stateful objects (Values or Computeds), and the UI will automatically update when the state changes.
</>
),
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Compose = Chemical.Compose
local OnEvent = Chemical.OnEvent
local Children = Chemical.Symbols.Children
-- Assuming playerGui is defined: local playerGui = game.Players.LocalPlayer:WaitForChild("PlayerGui")
local isVisible = Value(true)
local buttonText = Value("Click Me")
local MyScreen = Compose("ScreenGui")({
Parent = playerGui,
[Children] = {
Compose("TextButton")({
Text = buttonText, -- Bound to the buttonText Value
Visible = isVisible, -- Bound to the isVisible Value
Size = UDim2.new(0, 100, 0, 50),
-- Parent is implicitly MyScreen due to [Children] structure
[OnEvent("Activated")] = function()
print("Button clicked!")
isVisible:set(false) -- Change state, UI updates
end
})
}
})
buttonText:set("Submit") -- Button's text updates automatically
`,
},
],
},
{
title: "Networking with Reactors",
entries: [
{
signature: "Reactions and Reactors",
description: (
<>
For networked applications, Chemical provides <Link to="/api/reactor" className="text-purple-400 hover:underline">Reactors</Link> and <Link to="/api/reaction" className="text-purple-400 hover:underline">Reactions</Link>.
A Reactor is a server-side factory for creating Reactions. Reactions are stateful objects whose state can be replicated from the server to clients.
Changes made to a Reaction on the server are automatically propagated to subscribed clients. Reaction properties are accessed directly.
</>
),
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Reactor = Chemical.Reactor
local Effect = Chemical.Effect
-- Server-side: Define a Reactor for player scores
local ScoreReactor = Reactor({ Name = "PlayerScore" }, function(playerIdString)
-- 'playerIdString' (the key) is available but typically not stored in the returned table.
return {
currentScore = Value(0)
}
end)
-- Create a Reaction for a specific player
local player1ScoreReaction = ScoreReactor:create("player123")
-- Access Reaction properties directly:
player1ScoreReaction.currentScore:set(100) -- This change will be sent to clients
-- Client-side: Await and use the Reaction
local player1ScoreClientReaction = ScoreReactor:await("player123") -- Yields
if player1ScoreClientReaction then
Effect(function()
-- Access Reaction properties directly:
print("Player 123 score on client:", player1ScoreClientReaction.currentScore:get())
end)
end
`,
},
],
},
{
title: "Underlying ECS",
entries: [
{
signature: "Entity-Component-System",
description: (
<>
Chemical internally uses an Entity-Component-System (ECS) architecture (specifically `JECS`) to manage its reactive objects and their relationships.
While direct interaction with the ECS is usually not required for typical use cases, understanding this can be helpful for advanced scenarios or debugging.
Each reactive object created by Chemical (Value, Computed, Effect, etc.) is an "entity" with associated "components" (like its current value, previous value, or callback functions) and "tags" (like `IsStateful`, `IsDirty`).
</>
),
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
-- This is a conceptual example, direct ECS manipulation is advanced.
local myValue = Value(10)
-- myValue.entity is the ECS entity ID.
-- Chemical.World:get(myValue.entity, Chemical.ECS.Components.Value) would return 10.
-- When myValue:set(20) is called, Chemical.World:set(...) updates the component
-- and adds the Chemical.ECS.Tags.IsDirty tag.
-- The Scheduler then processes entities with IsDirty.
`,
},
],
}
];
return <ContentPage title="Core Concepts" introduction={intro} sections={sections} />;
};