### Clone and Install Luau Example Source: https://github.com/littensy/reflex/blob/master/docs/docs/quick-start/examples.md Clone the Luau example repository, install dependencies, and set up sourcemaps. ```bash git clone https://github.com/littensy/reflex-example-luau.git cd reflex-example-luau wally install rojo sourcemap -o sourcemap.json wally-package-types --sourcemap sourcemap.json Packages ``` -------------------------------- ### Clone and Build TypeScript Example Source: https://github.com/littensy/reflex/blob/master/docs/docs/quick-start/examples.md Clone the TypeScript example repository, install dependencies, and build the project. ```bash git clone https://github.com/littensy/reflex-example-ts.git cd reflex-example-ts npm install npm run build ``` -------------------------------- ### Installation Source: https://context7.com/littensy/reflex/llms.txt Instructions for installing the Reflex library using npm or Wally. ```bash npm install @rbxts/reflex ``` ```toml # wally.toml [dependencies] Reflex = "littensy/reflex@4.3.1" ``` -------------------------------- ### Broadcaster Setup and Middleware Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md This snippet demonstrates the basic setup of the Reflex broadcaster, including applying its middleware to the root producer. ```APIDOC ## Broadcaster Setup and Middleware ### Description Sets up a broadcaster to send shared actions to clients and applies the broadcaster's middleware to the root producer. ### Method N/A (Configuration) ### Endpoint N/A (Configuration) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript import { producer } from "./producer"; const broadcaster = createBroadcaster({ producers: slices, dispatch: (player, actions) => { remotes.dispatch.fire(player, actions); }, }); remotes.start.connect((player) => { broadcaster.start(player); }); producer.applyMiddleware(broadcaster.middleware); ``` ### Response None #### Success Response (200) None #### Response Example None ``` -------------------------------- ### Start Local Development Server Source: https://github.com/littensy/reflex/blob/master/docs/README.md Starts a local development server for live preview. Changes are reflected without a server restart. ```bash yarn start ``` -------------------------------- ### Example Output of Todo Subscription Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/your-first-producer.md This is the expected output after running the todo subscription example. ```text --> TODO: Buy milk, Buy eggs ``` -------------------------------- ### Broadcaster Start Method Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Starts broadcasting state to a specific player. ```APIDOC ## broadcaster.start(player) ### Description Call the `start` method when a player indicates that it is ready to receive state. ### Method `broadcaster.start(player)` ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **player** (Player) - Required - The player object to start broadcasting state to. ### Request Example ```typescript remotes.start.connect((player) => { broadcaster.start(player); }); ``` ### Response None ``` -------------------------------- ### Install Dependencies with Yarn Source: https://github.com/littensy/reflex/blob/master/docs/README.md Run this command to install project dependencies using Yarn. ```bash yarn ``` -------------------------------- ### Basic Usage of useProducer Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/use-producer.md Example demonstrating how to import and use the useProducer hook in a function component to get the producer. ```APIDOC ## Basic Usage of `useProducer` ### Description Call `useProducer` from a component to reference your app's [producer](../reflex/producer). ### Method Function Hook ### Endpoint N/A (Hook) ### Request Example ```typescript import { useProducer } from "@rbxts/roact-reflex"; import { RootProducer } from "./producer"; function Button() { const producer = useProducer(); // ... } ``` ### Response `useProducer` returns the [producer](../reflex/producer) you passed to the [``](reflex-provider) component. It doesn't matter how many components are between your component and the provider because of Roact's [context](https://roblox.github.io/roact/advanced/context/). ### Response Example ```typescript // producer object { // ... producer methods and state } ``` ``` -------------------------------- ### Start Broadcaster and Apply Middleware Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Connect the broadcaster's start method to a player-ready event and apply its middleware to the root producer. ```typescript remotes.start.connect((player) => { broadcaster.start(player); }); producer.applyMiddleware(broadcaster.middleware); ``` ```lua remotes.start:connect(function(player) broadcaster:start(player) end) producer:applyMiddleware(broadcaster.middleware) ``` -------------------------------- ### Usage Examples Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/use-selector.md Examples demonstrating how to use the useSelector hook for subscribing to state and implementing custom equality comparisons. ```APIDOC ## Usage ### Subscribing to a producer's state Call `useSelector` from a function component to read a value from the producer's state: ```ts import { useSelector } from "@rbxts/roact-reflex"; import { selectTodos } from "./selectors"; function Todos() { // highlight-next-line const todos = useSelector(selectTodos); // ... } ``` `Todos` will re-render whenever `selectTodos` returns a new value. Functionally, `useSelector` subscribes to the producer's state, and re-renders when the selected value changes. You can then render a list of todos from the selected value: ```tsx function Todos() { const todos = useSelector(selectTodos); return ( // highlight-start {todos.map((todo) => ( ))} // highlight-end ); } ``` ### Custom equality comparison By default, `useSelector` uses `===` to compare the previous and next values. You can customize this behavior with the `isEqual` parameter. For example, some components might want to receive the latest state **only if it's defined** and exclude `undefined` values. You can write an equality function that compares the current and previous values, and returns `true` if the new value is `undefined`: ```ts import { useSelector } from "@rbxts/roact-reflex"; import { selectValue } from "./selectors"; function isEqualOrUndefined(current: unknown, previous: unknown) { return current === previous || current === undefined; } function Button() { const value = useSelector(selectValue, isEqualOrUndefined); // ... } ``` Remember that if the equality function returns `true`, the component **will not** re-render for that state update. ``` -------------------------------- ### Install Reflex with Yarn Source: https://github.com/littensy/reflex/blob/master/docs/docs/quick-start/installation.md Use this command to install the Reflex library for your Roblox project via Yarn. ```bash yarn add @rbxts/reflex ``` -------------------------------- ### Install @rbxts/reflex Source: https://github.com/littensy/reflex/blob/master/README.md Install the Reflex package using npm, yarn, or pnpm. ```bash npm install @rbxts/reflex yarn add @rbxts/reflex pnpm add @rbxts/reflex ``` -------------------------------- ### Usage Example (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/combine-producers.md Demonstrates how to dispatch actions on the combined root producer. Ensure the producer is imported correctly. ```typescript import { producer } from "./producer"; producer.setPage("leaderboard"); // --> { router: { page: "leaderboard" }, ... } producer.addPlayer(123); // --> { leaderboard: { players: [123] }, ... } ``` -------------------------------- ### Install Reflex with pnpm Source: https://github.com/littensy/reflex/blob/master/docs/docs/quick-start/installation.md Use this command to install the Reflex library for your Roblox project via pnpm. ```bash pnpm add @rbxts/reflex ``` -------------------------------- ### Install Reflex with npm Source: https://github.com/littensy/reflex/blob/master/docs/docs/quick-start/installation.md Use this command to install the Reflex library for your Roblox project via npm. ```bash npm install @rbxts/reflex ``` -------------------------------- ### Usage Example (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/combine-producers.md Demonstrates dispatching actions on the combined root producer in Luau. Ensure the producer is correctly required. ```luau local Reflex = require(ReplicatedStorage.Packages.Reflex) local producer = require(script.Parent.producer) producer.setPage("leaderboard") --> { router = { page = "leaderboard" }, ... } producer.addPlayer(123) --> { leaderboard = { players = { 123 } }, ... } ``` -------------------------------- ### Start Broadcaster Connection (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Connects to the 'start' remote event and calls broadcaster:start for the player. This ensures players are ready before receiving state and actions. ```Luau remotes.start:connect(function(player) broadcaster:start(player) end) ``` -------------------------------- ### Install Roact Reflex with npm Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/roact-reflex/index.md Install the official Roact Reflex bindings using npm. This is the first step to integrate Reflex with Roact. ```bash npm install @rbxts/roact-reflex ``` -------------------------------- ### Install Roact Reflex with pnpm Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/roact-reflex/index.md Install the official Roact Reflex bindings using pnpm. This is another alternative to npm for package management. ```bash pnpm add @rbxts/roact-reflex ``` -------------------------------- ### Install Roact Reflex with Yarn Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/roact-reflex/index.md Install the official Roact Reflex bindings using Yarn. This is an alternative to npm for package management. ```bash yarn add @rbxts/roact-reflex ``` -------------------------------- ### Using Custom Root Provider Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/reflex-provider.md Example of mounting the application using a custom RootProvider, which internally handles the ReflexProvider and other necessary providers. ```tsx import Roact from "@rbxts/roact"; import { RootProvider } from "./RootProvider"; Roact.mount( , Players.LocalPlayer.WaitForChild("PlayerGui"), ); ``` -------------------------------- ### Basic Producer Setup with Logger Middleware Source: https://context7.com/littensy/reflex/llms.txt Demonstrates creating a producer and applying the built-in logger middleware. The logger middleware outputs state changes and dispatched actions. ```typescript import { createProducer, loggerMiddleware, ProducerMiddleware } from "@rbxts/reflex"; const producer = createProducer({ count: 0 }, { increment: (state, amount: number) => ({ count: state.count + amount }), decrement: (state, amount: number) => ({ count: state.count - amount }), }); // Built-in logger middleware producer.applyMiddleware(loggerMiddleware); // Output: // [Reflex] Mounted with state { count: 0 } // [Reflex] Dispatching increment(5) // [Reflex] State changed to { count: 5 } ``` -------------------------------- ### Incorrectly Subscribing to Todo Slice (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/combine-producers.md This Luau example demonstrates an incorrect subscription setup that will not fire when expected. Ensure subscriptions are correctly linked to the root producer's state changes. ```Luau local producer = require(script.Parent.producer) -- ⚠️ This will not fire when the previous example runs producer.subscribe(selectTodos, function(todos) print(state.todos) end) ``` -------------------------------- ### Correctly Subscribing to Root Producer (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/combine-producers.md This Luau example demonstrates the correct way to set up a subscription that will fire when the root state changes. Ensure subscriptions are correctly linked to the root producer. ```Luau local producer = require(script.Parent.producer) -- ✅ This will fire when the previous example runs producer.subscribe(selectTodos, function(todos) print(state.todos) end) ``` -------------------------------- ### Start Broadcaster Connection (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Connects to the 'start' remote event and calls broadcaster.start for the player. This ensures players are ready before receiving state and actions. ```TypeScript remotes.start.connect((player) => { broadcaster.start(player); }); ``` -------------------------------- ### Install Reflex with Wally Source: https://context7.com/littensy/reflex/llms.txt Add Reflex as a dependency in your wally.toml file. This ensures Reflex is available for your Luau project. ```toml # wally.toml [dependencies] Reflex = "littensy/reflex@4.3.1" ``` -------------------------------- ### Incorrectly Subscribing to Todo Slice (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/combine-producers.md This example demonstrates an incorrect subscription setup that will not fire when expected. Ensure subscriptions are correctly linked to the root producer's state changes. ```TypeScript import { producer } from "./producer"; // ⚠️ This will not fire when the previous example runs producer.subscribe(selectTodos, (todos) => { print(state.todos); }); ``` -------------------------------- ### Dispatching Actions with useProducer Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/use-producer.md Example showing how to destructure actions from the producer obtained via useProducer for direct dispatching. ```APIDOC ## Dispatching Actions ### Description This example shows how to destructure actions from the producer obtained via `useProducer` for direct dispatching. ### Method Function Hook ### Endpoint N/A (Hook) ### Request Example ```typescript import { useProducer } from "@rbxts/roact-reflex"; import { RootProducer } from "./producer"; function Button() { const { increment } = useProducer(); return increment(1) }} />; } ``` ``` -------------------------------- ### Correctly Subscribing to Root Producer (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/combine-producers.md This TypeScript example demonstrates the correct way to set up a subscription that will fire when the root state changes. Ensure subscriptions are correctly linked to the root producer. ```TypeScript import { producer } from "./producer"; // ✅ This will fire when the previous example runs producer.subscribe(selectTodos, (todos) => { print(state.todos); }); ``` -------------------------------- ### Filtering Actions with beforeDispatch Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Example of using the `beforeDispatch` option to filter actions before they are sent to clients. ```APIDOC ## Filtering Actions with beforeDispatch ### Description Uses the `beforeDispatch` option to filter or modify actions before they are sent to the client. This example prevents a 'sensitive' action from being dispatched if the player's name is not the first argument. ### Method N/A (Configuration) ### Endpoint N/A (Configuration) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript const broadcaster = createBroadcaster({ producers: slices, dispatch: (player, actions) => { remotes.dispatch.fire(player, actions); }, beforeDispatch: (player, action) => { if (action.name === "sensitive" && action.arguments[0] !== player.Name) { return; } return action; }, }); ``` ### Response None #### Success Response (200) None #### Response Example None ``` -------------------------------- ### Usage: Updating state with actions Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-producer.md Examples demonstrating how to use createProducer to initialize state and update it via actions in TypeScript and Luau. ```APIDOC ## Usage: Updating state with actions ### Description Producers are the state containers that you can use to dispatch actions and observe state changes. They work with _immutable data_, ensuring safety and predictability when working with state. Typically, games and apps keep all of their state in a single root producer. This allows them to easily observe and modify any part of the state. Use `createProducer` to create a producer with an initial state and action functions: ### TypeScript Example ```ts interface CounterState { readonly count: number; } const initialState: CounterState = { count: 0, }; const producer = createProducer(initialState, { increment: (state, value: number) => ({ ...state, count: state.count + value, }), // ... }); producer.increment(1); producer.getState(); // { count: 1 } ``` ### Luau Example ```lua type CounterState = { count: number, } type CounterActions = { increment: (value: number) -> (), -- ... } local initialState: CounterState = { count = 0, } local producer = Reflex.createProducer(initialState, { increment = function(state, value: number): CounterState return { count = state.count + value } end, -- ... }) :: Reflex.Producer producer.increment(1) producer:getState() --> { count = 1 } ``` ### Note on Immutable Data Libraries See libraries like [Sift](https://csqrl.github.io/sift/) and [Immut](https://solarhorizon.github.io/immut/) for utilities that make it easier to work with immutable data. ``` -------------------------------- ### Basic ReflexProvider Setup Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/reflex-provider.md Wrap your application's root component with ReflexProvider to make a Reflex producer available to all nested components. This is essential for using Reflex Hooks. ```tsx ``` -------------------------------- ### Correctly Adding to Root Producer (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/combine-producers.md This Luau example shows the correct way to add an item to the root state by calling the action directly on the root producer. This ensures the state is updated. ```Luau local producer = require(script.Parent.producer) -- ✅ This will update the root state producer.add("Hello world!") ``` -------------------------------- ### Apply Middleware to Luau Producer Source: https://github.com/littensy/reflex/blob/master/docs/docs/advanced-guides/middleware.md Applies the custom `doubleMiddleware` and `loggerMiddleware` to a Reflex producer. This example shows how the middleware order affects the dispatched action arguments. ```luau local counter = Reflex.createProducer(0, { set = function(state, value: number) return value end, increment = function(state, amount: number) return state + amount end, }) counter:applyMiddleware(doubleMiddleware, loggerMiddleware) counter.set(5) counter.increment(1) ``` -------------------------------- ### Create a basic producer in Luau Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/organizing-producers.md Use `Reflex.createProducer` to define state and actions for a producer in Luau. This example demonstrates adding and removing items from a list within the state. ```Luau local Reflex = require(ReplicatedStorage.Packages.Reflex) -- ... local todos = Reflex.createProducer(initialState, { addTodo = function(state: TodosState, todo: string): TodosState local nextState = table.clone(state) local nextTodos = table.clone(state.todos) table.insert(nextTodos, todo) nextState.todos = nextTodos return nextState end, removeTodo = function(state: TodosState, todo: string): TodosState local nextState = table.clone(state) local nextTodos = table.clone(state.todos) table.remove(nextTodos, table.find(nextTodos, todo) or -1) nextState.todos = nextTodos return nextState end, }) :: TodosProducer ``` -------------------------------- ### Defining a Reflex Slice with Actions Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/roact-reflex/using-the-producer.md Define a producer slice with its initial state and actions. This example shows a 'todos' slice with an 'addTodo' action. ```ts import { createProducer } from "@rbxts/reflex"; interface TodosState { readonly list: readonly string[]; } const initialState: TodosState = { list: [], }; export const todosSlice = createProducer(initialState, { addTodo: (state, todo: string) => ({ ...state, list: [...state.list, todo], }), }); ``` -------------------------------- ### Subscribe to Todo Changes (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/your-first-producer.md Subscribe to changes in the 'todos' state and print the updated list. This example requires the 'todos' producer to be imported. ```luau local todos = require(script.Parent.todos) local function selectTodos(state: todos.TodosState) return state.todos end todos:subscribe(selectTodos, function(todos) print("TODO: " .. table.concat(todos, ", ")) end) todos.addTodo("Buy milk") todos.addTodo("Buy eggs") ``` -------------------------------- ### Create a Client-Side Broadcast Receiver Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/index.md Use `createBroadcastReceiver` on the client to synchronize state with the server. The `start` function is called to initiate the synchronization process. ```typescript const receiver = createBroadcastReceiver({ start: () => { remotes.start.fire(0); }, }); ``` -------------------------------- ### Correctly Adding to Root Producer (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/combine-producers.md This TypeScript example shows the correct way to add an item to the root state by calling the action directly on the root producer. This ensures the state is updated. ```TypeScript import { producer } from "./producer"; // ✅ This will update the root state producer.add("Hello world!"); ``` -------------------------------- ### Usage Example: Subscribing to state with selector factories Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/use-selector-creator.md Demonstrates how to use useSelectorCreator with a selector factory to efficiently subscribe to state that depends on external arguments, preventing unnecessary re-renders. ```APIDOC ## Usage: Subscribing to state with selector factories [Selector factories](../reflex/create-selector#selector-factories) are useful for creating selectors that depend on external arguments. For example, a selector that selects a todo by its ID might look like this: ```ts const selectTodo = (id: number) => { return createSelector(selectTodos, (todos) => { return todos.find((todo) => todo.id === id); }); }; ``` Roact Reflex provides [`useSelector`](use-selector) to connect a function component to selectors, but it's not safe to create a selector inside of a component without memoizing it. This is because the selector will be re-created on every render, which will create a new cache, causing further re-renders. To solve this, you might try to memoize the selector with the `useMemo` hook: ```ts import { selectTodo } from "./selectors"; function Todo({ id }: Props) { // highlight-start const selector = useMemo(() => { return selectTodo(id); }, [id]); // highlight-end const todo = useSelector(selector); // ... } ``` This works, and it's essentially what [`useSelectorCreator`](#useselectorcreatorfactory-args) does. It creates a selector with the given arguments, memoizes it with the arguments you passed, and subscribes to the selector's state. ```ts import { useSelectorCreator } from "@rbxts/roact-reflex"; import { selectTodo } from "./selectors"; function Todo({ id }: Props) { const todo = useSelectorCreator(selectTodo, id); // ... } ``` ``` -------------------------------- ### ReflexProvider Setup Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/reflex-provider.md The ReflexProvider is the root component for Roact Reflex. It makes a Reflex producer available to the rest of your app, allowing components to access and manage state using Hooks. ```APIDOC ## ReflexProvider Component ### Description The `` component is essential for initializing Roact Reflex in your application. It accepts a `producer` prop, which is the root Reflex producer that will manage your application's state. By wrapping your root component with ``, you make the state and actions accessible to any nested components through Reflex Hooks. ### Props - **producer** (object) - Required - The root Reflex producer to use for the rest of the app. ### Usage Wrap your root Roact component with `` to enable Reflex functionality. ```tsx import Roact from "@rbxts/roact"; import { ReflexProvider } from "@rbxts/roact-reflex"; // Assume 'producer' is defined elsewhere const producer = /* ... your producer definition ... */; Roact.mount( , Players.LocalPlayer.WaitForChild("PlayerGui") ); ``` ### Nesting Providers If you are using other providers alongside ``, you can nest them. It's often beneficial to create a custom provider that combines all necessary providers. **Example with custom provider:** ```tsx // RootProvider.tsx import Roact from "@rbxts/roact"; import { ReflexProvider } from "@rbxts/roact-reflex"; // Assume 'producer' and 'OtherProvider' are defined elsewhere const producer = /* ... your producer definition ... */; const OtherProvider = (props: Roact.PropsWithChildren) => <>{props[Roact.Children]}; export function RootProvider(props: Roact.PropsWithChildren) { return ( {props[Roact.Children]} ); } // main.client.tsx import Roact from "@rbxts/roact"; import { RootProvider } from "./RootProvider"; const App = () =>
My App
; Roact.mount( , Players.LocalPlayer.WaitForChild("PlayerGui") ); ``` ### Caveats - You should only use `` once at the root of your application. - Refer to the documentation on [using a root producer](../reflex/combine-producers#using-multiple-producers) for more details. ``` -------------------------------- ### Use Producer Actions and Selectors Source: https://github.com/littensy/reflex/blob/master/README.md Dispatch actions by calling them directly on the producer and subscribe to state changes using selectors. This example shows how to subscribe to and print the count. ```typescript const selectCount = (state: State) => state.count; producer.subscribe(selectCount, (count) => { print(`The count is now ${count}`); }); producer.increment(); // The count is now 1 ``` -------------------------------- ### Non-idempotent action in Luau Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md A Luau example of a non-idempotent action that generates a GUID within the function, causing potential state divergence. ```Luau local producer = Reflex.createProducer(initialState, { addItem = function(state) local nextState = table.clone(state) // error-next-line -- 🔴 Syncing this action will cause state to diverge // error-next-line table.insert(nextState.items, { // error-next-line id = HttpService:GenerateGUID(), // error-next-line name = "New Item" // error-next-line }) return nextState end, }) ``` -------------------------------- ### Set up Broadcaster with Default Options Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Initializes a broadcaster with shared producers and a dispatch function to send actions to clients. Apply the broadcaster's middleware to your root producer. ```typescript import { producer } from "./producer"; const broadcaster = createBroadcaster({ producers: slices, dispatch: (player, actions) => { remotes.dispatch.fire(player, actions); }, }); remotes.start.connect((player) => { broadcaster.start(player); }); producer.applyMiddleware(broadcaster.middleware); ``` ```lua local Reflex = require(ReplicatedStorage.Packages.Reflex) local remotes = require(ReplicatedStorage.shared.remotes) local slices = require(ReplicatedStorage.shared.slices) local producer = require(script.Parent.producer) local broadcaster = Reflex.createBroadcaster({ producers = slices, dispatch = function(player, actions) remotes.dispatch:fire(player, actions) end, }) remotes.start:connect(function(player) broadcaster:start(player) end) producer:applyMiddleware(broadcaster.middleware) ``` -------------------------------- ### broadcaster.start(player) Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Marks a player as ready to receive state and actions, and sends the current shared state to the client for hydration. Players will only be passed to options.dispatch if they have notified the server with start. This is to prevent sending actions to players who are not ready to receive them. ```APIDOC ## POST /broadcaster/start ### Description Marks a player as ready to receive state and actions, and sends the current shared state to the client for hydration. ### Method POST ### Endpoint /broadcaster/start ### Parameters #### Path Parameters - **player** (Player) - Required - The player who requested state. Should be received from a remote event call. ### Request Example ```json { "player": "player_id_or_object" } ``` ### Response #### Success Response (200) - **status** (string) - Indicates success. #### Response Example ```json { "status": "success" } ``` ``` -------------------------------- ### Memoized Selector Example Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-selector.md This is an example of a memoized selector that efficiently retrieves items in stock. It only re-computes the list if the state.items change. ```javascript const selectInStock = createSelector( [state => state.items], items => items.filter(item => item.in_stock) ); // Example usage: const stock = selectInStock(state); print("Items available:", stock) ``` -------------------------------- ### Build Static Website Content Source: https://github.com/littensy/reflex/blob/master/docs/README.md Generates static website files into the 'build' directory, ready for hosting. ```bash yarn build ``` -------------------------------- ### Create Broadcaster (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/advanced-guides/server-client-sync.md Set up a broadcaster on the server to dispatch shared actions to clients. Ensure remotes are configured and apply the broadcaster's middleware to your root producer. ```typescript import { createBroadcaster } from "@rbxts/reflex"; import { remotes } from "shared/remotes"; import { slices } from "shared/slices"; import { producer } from "./producer"; const broadcaster = createBroadcaster({ producers: slices, dispatch: (player, actions) => { remotes.dispatch.fire(player, actions); }, }); remotes.start.connect((player) => { broadcaster.start(player); }); producer.applyMiddleware(broadcaster.middleware); ``` -------------------------------- ### Create a Producer with Actions (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/your-first-producer.md Define initial state and actions to update it immutably. Use this for managing application state in Reflex. ```luau local Reflex = require(ReplicatedStorage.Packages.Reflex) export type TodosProducer = Reflex.Producer export type TodosState = { todos: { string }, } export type TodosActions = { addTodo: (todo: string) -> (), removeTodo: (todo: string) -> (), } local initialState: TodosState = { todos = {}, } local todos = Reflex.createProducer(initialState, { addTodo = function(state: TodosState, todo: string): TodosState local nextState = table.clone(state) local nextTodos = table.clone(state.todos) table.insert(nextTodos, todo) nextState.todos = nextTodos return nextState end, removeTodo = function(state: TodosState, todo: string): TodosState local nextState = table.clone(state) local nextTodos = table.clone(state.todos) table.remove(nextTodos, table.find(nextTodos, todo) or -1) nextState.todos = nextTodos return nextState end, }) :: TodosProducer return todos ``` -------------------------------- ### Get Current State from Producer Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-producer.md Retrieves the current state managed by the producer. This is useful for observing changes or debugging. ```typescript producer.increment(1); producer.getState(); // state = 1 ``` ```luau producer.increment(1) producer:getState() --> { count = 1 } ``` -------------------------------- ### Get Producer State Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/producer.md Retrieve the current state of the producer. A selector function can be provided to extract a specific part of the state. ```typescript producer.getState(); // { count: 0 } producer.getState((state) => state.count); // 0 ``` ```lua producer:getState() --> { count = 0 } producer:getState(function(state) return state.count end) --> 0 ``` -------------------------------- ### Select Players by ID (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/observers-and-entities.md Use this selector to get a record of players by their ID from the state. It requires the `RootState` type. ```typescript import { RootState } from "./producer"; export const selectPlayersById = (state: RootState) => { return state.players.entities; }; ``` -------------------------------- ### Non-idempotent action in TypeScript Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md This action function is not idempotent because it generates a GUID internally, which will lead to state divergence between client and server. ```TypeScript const producer = createProducer(initialState, { addItem: (state) => ({ ...items, // error-next-line // 🔴 Syncing this action will cause state to diverge // error-next-line { id: HttpService.GenerateGUID(), name: "New Item" }, }) }) ``` -------------------------------- ### Applying Multiple Middlewares Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/middleware.md Demonstrates how to apply multiple middleware functions to a producer. The order of application determines the order in which they are executed. ```typescript producer.applyMiddleware(firstMiddleware, secondMiddleware, thirdMiddleware); ``` -------------------------------- ### Set up ReflexProvider in Roact Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/roact-reflex/index.md Mount the root component with ReflexProvider to enable Reflex Hooks for a producer. Ensure the producer is correctly passed as a prop. ```tsx Roact.mount( , playerGui, ); ``` -------------------------------- ### Creating a Custom Root Provider Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/reflex-provider.md This custom provider component encapsulates ReflexProvider and other providers, simplifying the root component setup for your application. ```tsx import Roact from "@rbxts/roact"; export function RootProvider(props: Roact.PropsWithChildren) { return ( {props[Roact.Children]} ); } ``` -------------------------------- ### Get Producer Actions Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/producer.md Retrieve the action functions associated with the producer. This can be used for advanced scenarios like filtering actions in a broadcaster. ```typescript const actions = producer.getActions(); ``` ```lua local actions = producer:getActions() ``` -------------------------------- ### Create Broadcaster (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/advanced-guides/server-client-sync.md Initialize a broadcaster on the server to send shared actions to clients. This requires setting up remotes and applying the broadcaster's middleware to the root producer. ```luau local Reflex = require(ReplicatedStorage.Packages.Reflex) local remotes = require(ReplicatedStorage.shared.remotes) local slices = require(ReplicatedStorage.shared.slices) local producer = require(script.Parent.producer) local broadcaster = Reflex.createBroadcaster({ producers = slices, dispatch = function(player, actions) remotes.dispatch:fire(player, actions) end, }) remotes.start:connect(function(player) broadcaster:start(player) end) producer:applyMiddleware(broadcaster.middleware) ``` -------------------------------- ### Create Broadcast Receiver (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/advanced-guides/server-client-sync.md Initializes client state with server's shared state and keeps it in sync. Requires Reflex module. ```luau local Reflex = require(ReplicatedStorage.Packages.Reflex) local receiver = Reflex.createBroadcastReceiver({ start = function() remotes.start.fire() end, }) remotes.dispatch:connect(function(actions) receiver:dispatch(actions) end) producer:applyMiddleware(receiver.middleware) ``` -------------------------------- ### Subscribe to Todo Changes (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/your-first-producer.md Subscribe to changes in the 'todos' state and print the updated list. This example requires the 'todos' producer to be imported. ```typescript import { TodosState, todos } from "./todos"; const selectTodos = (state: TodosState) => state.todos; todos.subscribe(selectTodos, (todos) => { print(`TODO: ${todos.join(", ")}`); }); todos.addTodo("Buy milk"); todos.addTodo("Buy eggs"); ``` -------------------------------- ### Create a Producer with Initial State and Actions Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-producer.md Initializes a producer with a given state and a set of actions. Actions are pure functions that update the state immutably. ```typescript const producer = createProducer(0, { increment: (state, value: number) => state + value, decrement: (state, value: number) => state - value, set: (_, value: number) => value, }); ``` ```luau type Actions = { increment: (value: number) -> () } local producer = Reflex.createProducer(0, { increment = function(state, value: number): number return state + value end, decrement = function(state, value: number): number return state - value end, set = function(_, value: number): number return value end, }) :: Reflex.Producer ``` -------------------------------- ### Create a Broadcaster Instance Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Instantiate a broadcaster with options. The dispatch function is called when actions are ready to be sent to clients. ```typescript import { createBroadcaster } from "@rbxts/reflex"; const broadcaster = createBroadcaster({ producers: slices, dispatch: (player, actions) => { // using @rbxts/remo remotes.dispatch.fire(player, actions); }, }); ``` ```lua local Reflex = require(ReplicatedStorage.Packages.Reflex) local broadcaster = Reflex.createBroadcaster({ producers = producers, dispatch = function(player, actions) -- using Remo remotes.dispatch:fire(player, actions) end, }) ``` -------------------------------- ### Accessing the root producer with useProducer Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/use-producer.md Use the useProducer hook within a function component to get the producer instance. This producer is passed to the ReflexProvider. ```typescript import { useProducer } from "@rbxts/roact-reflex"; import { RootProducer } from "./producer"; function Button() { const producer = useProducer(); // ... } ``` -------------------------------- ### Select Calendar Events Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/using-selectors.md A simple selector function to retrieve all events from the calendar slice. Use `producer.getState` to get the current value of the selector. ```TypeScript const selectEvents = (state: RootState) => { return state.calendar.events; }; for (const event of producer.getState(selectEvents)) { print(`${event.name} (${event.date})`); } ``` ```Luau local function selectEvents(state: producer.RootState) return state.calendar.events end for _, event in producer:getState(selectEvents) do print("- " .. event.name .. " (" .. event.date .. ")") end ``` -------------------------------- ### Troubleshooting: useProducer must be called from within a ReflexProvider Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/use-producer.md Explains the common error 'useProducer must be called from within a ReflexProvider' and provides the solution by wrapping components in ReflexProvider. ```APIDOC ## Troubleshooting: `useProducer` must be called from within a `ReflexProvider` ### Description This error means that you're trying to use [`useProducer`](#useproducert) in a function component that isn't wrapped in a [``](reflex-provider). Roact Reflex uses Roact contexts to pass the producer to your components. ### Method Component Wrapping ### Endpoint N/A (Conceptual Fix) ### Request Example (Incorrect Usage) ```typescript function App() { const producer = useProducer(); // ... } // error-next-line // 🔴 You need to wrap your root elements in a Roact.mount(, container); ``` ### Response (Correct Usage) Wrap your root elements in a ``: ### Response Example ```typescript function App() { const producer = useProducer(); // ... } Roact.mount( // highlight-start , // highlight-end container, ); ``` ``` -------------------------------- ### Custom Equality Comparison with useSelector Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/roact-reflex/use-selector.md Customize the state comparison logic by providing an 'isEqual' function to useSelector. This example re-renders only if the new value is not undefined. ```typescript import { useSelector } from "@rbxts/roact-reflex"; import { selectValue } from "./selectors"; function isEqualOrUndefined(current: unknown, previous: unknown) { return current === previous || current === undefined; } function Button() { const value = useSelector(selectValue, isEqualOrUndefined); // ... } ``` -------------------------------- ### Create an Optimized Selector Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/index.md Use `createSelector` to create memoized selectors that only recompute when their dependencies change. This example selects items with stock greater than 0. ```typescript const selectItems = (state: State) => state.items; const selectInStock = createSelector(selectItems, (items) => { return items.filter((item) => item.stock > 0); }); ``` -------------------------------- ### Conditional Subscription with Predicate (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/subscribing-to-state.md Use producer.subscribe with a predicate to only run the listener when the predicate returns true. This example only plays a sound if health decreases. ```typescript const didDecrease = (current: number, previous: number) => { return current < previous; }; producer.subscribe(selectHealth, didDecrease, (health, lastHealth) => { // Play sound }); ``` -------------------------------- ### Create a Producer with Actions (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/your-first-producer.md Define initial state and actions to update it immutably. Use this for managing application state in Reflex. ```typescript import { createProducer } from "@rbxts/reflex"; export interface TodosState { readonly todos: readonly string[]; } const initialState: TodosState = { odos: [], }; export const todos = createProducer(initialState, { addTodo: (state, todo: string) => ({ ...state, todos: [...state.todos, todo], }), removeTodo: (state, todo: string) => ({ ...state, todos: state.todos.filter((t) => t !== todo), }), }); ``` -------------------------------- ### createBroadcaster Options Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/create-broadcaster.md Details the configuration options available for the `createBroadcaster` function. ```APIDOC ## createBroadcaster Options ### Description Configuration options for the `createBroadcaster` function. ### Method N/A (Configuration) ### Endpoint N/A (Configuration) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript const broadcaster = createBroadcaster({ producers: slices, // Your shared producer map. dispatch: (player, actions) => { // Callback to send actions to clients. remotes.dispatch.fire(player, actions); }, hydrateRate?: 60, // Optional: Rate for sending state for hydration. beforeDispatch?: (player, action) => action, // Optional: Filter actions before dispatch. beforeHydrate?: (player, state) => state, // Optional: Filter state before hydration. }); ``` ### Response Returns a broadcaster object with `middleware` and `start` properties. #### Success Response (200) - **middleware** (object) - Reflex middleware for state synchronization. - **start** (function) - Method to call when a client is ready to receive actions and state. #### Response Example ```json { "middleware": "", "start": "" } ``` ``` -------------------------------- ### Create Calendar Slice Producer Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/using-selectors.md Defines a producer for the calendar slice, including an action to add events. This is the initial setup for managing calendar state. ```TypeScript const calendarSlice = createProducer(initialState, { addEvent: (state, event: CalendarEvent) => ({ ...state, events: [...state.events, event], }), }); ``` ```Luau local calendarSlice = Reflex.createProducer(initialState, { addEvent = function(state: CalendarState, event: CalendarEvent): CalendarState local nextState = table.clone(state) local nextEvents = table.clone(state.events) table.insert(nextEvents, event) nextState.events = nextEvents return nextState end, }) ``` -------------------------------- ### Create a Producer Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/producer.md Initialize a producer with its initial state and a set of actions. ```typescript const producer = createProducer(initialState, actions); ``` -------------------------------- ### Apply Middleware to TypeScript Producer Source: https://github.com/littensy/reflex/blob/master/docs/docs/advanced-guides/middleware.md Applies the custom `doubleMiddleware` and `loggerMiddleware` to a Reflex producer. This example shows how the middleware order affects the dispatched action arguments. ```typescript const counter = createProducer(0, { set: (state, value: number) => value, increment: (state, amount: number) => state + amount, }); counter.applyMiddleware(doubleMiddleware, loggerMiddleware); counter.set(5); counter.increment(1); ``` -------------------------------- ### Custom Equality Check with createSelector Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/using-selectors.md Customize the equality check for `createSelector` using the `equalityCheck` option. This example uses `shallowEqual` to optimize when the combiner function is called. ```TypeScript import { createSelector, shallowEqual } from "@rbxts/reflex"; const selectTodos = createSelector( selectTodoIds, (ids) => { for (const _ of $range(0, 10000)) { // some expensive operation } }, // highlight-next-line { equalityCheck: shallowEqual }, ); selectTodos(table.clone(state)) === selectTodos(table.clone(state)); // true ``` ```Luau local Reflex = require(ReplicatedStorage.Packages.Reflex) local selectTodos = Reflex.createSelector(selectTodoIds, function(ids) for i = 1, 10000 do -- some expensive operation end end, { // highlight-next-line equalityCheck = Reflex.shallowEqual, }) selectTodos(table.clone(state)) == selectTodos(table.clone(state)) -- true ``` -------------------------------- ### Setup ReflexProvider for Roact Application Source: https://context7.com/littensy/reflex/llms.txt Wrap your Roact application's root component with ReflexProvider to make the producer context available to all child components. This enables the use of Reflex hooks like useProducer and useSelector. ```tsx import Roact from "@rbxts/roact"; import { ReflexProvider } from "@rbxts/roact-reflex"; import { clientProducer } from "./producer"; // Your root app component function App() { return ( ); } // Mount with ReflexProvider at root Roact.mount( , game.GetService("Players").LocalPlayer.WaitForChild("PlayerGui") ); // Can combine with other providers function RootProvider(props: Roact.PropsWithChildren) { return ( {props[Roact.Children]} ); } Roact.mount( , game.GetService("Players").LocalPlayer.WaitForChild("PlayerGui") ); ``` -------------------------------- ### Subscribe to Selector (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/using-selectors.md Subscribe to a selector to be notified of changes. This example shows how a selector that creates a new array on every call can trigger the listener unnecessarily. ```luau producer:subscribe(selectEventsByTime, function(events) print("events changed!") end) // highlight-next-line producer.addTodo("Unrelated todo") ``` -------------------------------- ### Apply Middleware in Reverse Order (Luau) Source: https://github.com/littensy/reflex/blob/master/docs/docs/advanced-guides/middleware.md Applies `loggerMiddleware` before `doubleMiddleware` to a Reflex producer. This demonstrates how the logger receives the original increment amount before transformation. ```luau producer:applyMiddleware(loggerMiddleware, doubleMiddleware) producer.set(5) producer.increment(1) ``` -------------------------------- ### Subscribe to Selector (TypeScript) Source: https://github.com/littensy/reflex/blob/master/docs/docs/guides/using-selectors.md Subscribe to a selector to be notified of changes. This example shows how a selector that creates a new array on every call can trigger the listener unnecessarily. ```typescript producer.subscribe(selectEventsByTime, (events) => { print("events changed!"); }); // highlight-next-line producer.addTodo("Unrelated todo"); ``` -------------------------------- ### Apply Middleware to a Producer Source: https://github.com/littensy/reflex/blob/master/docs/docs/reference/reflex/index.md Use `applyMiddleware` on a producer instance to add custom functionality like logging or action interception. This example applies `loggerMiddleware` and `customMiddleware`. ```typescript producer.applyMiddleware(loggerMiddleware, customMiddleware); ```