Skip to main content

useProducer

useProducer is a Reflex Hook that lets you use the producer you passed to <ReflexProvider> from your component.

const producer = useProducer<Producer>();

Reference

useProducer<T>()

Call useProducer from a function component to access the producer for the app.

import { useProducer } from "@rbxts/roact-reflex";
import { RootProducer } from "./producer";

function Button() {
const producer = useProducer<RootProducer>();
// ...
}

See more examples below.

Parameters

useProducer takes no parameters.

Returns

useProducer returns the Reflex producer passed to the <ReflexProvider> component. If there is more than one provider, the value is the producer from the closest provider above the component in the tree.

caveats
  • This is intended for dispatching actions. If you want to subscribe to state, prefer useSelector instead.

  • Your app should only have one root producer. Roact Reflex expects only one producer to be used in your components. Learn more about using a root producer.


Usage

Dispatching actions

Call useProducer from a component to reference your app's producer.

import { useProducer } from "@rbxts/roact-reflex";
import { RootProducer } from "./producer";

function Button() {
const producer = useProducer<RootProducer>();
// ...
}

useProducer returns the producer you passed to the <ReflexProvider> component. It doesn't matter how many components are between your component and the provider because of Roact's context.

You will mainly use this to dispatch actions to your producer. As a shorthand, you can destructure the producer into its actions:

function Button() {
const { increment } = useProducer<RootProducer>();

return <textbutton Event={{ Activated: () => increment(1) }} />;
}

To pass a producer to your components, see <ReflexProvider>.


Typed useProducer hook

On its own, useProducer doesn't know what type of producer you're using. It receives a generic type parameter that lets you specify the type of the producer, but that can be repetitive.

To reduce the amount of typing you have to do, we recommend using the UseProducerHook type:

import { UseProducerHook, useProducer } from "@rbxts/roact-reflex";

const useAppProducer: UseProducerHook<RootProducer> = useProducer;

Subscribing to state

To access the state from a Roact function component, see useSelector.


Troubleshooting

I'm getting an error: "useProducer must be called from within a ReflexProvider"

This error means that you're trying to use useProducer in a function component that isn't wrapped in a <ReflexProvider>:

function App() {
const producer = useProducer<RootProducer>();
// ...
}

// 🔴 You need to wrap your root elements in a <ReflexProvider>
Roact.mount(<App />, container);

Roact Reflex uses Roact contexts to pass the producer to your components and allow them to use Reflex Hooks. If you don't wrap your root elements in a <ReflexProvider>, your components won't be able to access the producer.

If your app or components use Reflex, you should wrap your root elements in a <ReflexProvider>:

function App() {
const producer = useProducer<RootProducer>();
// ...
}

// ✅ You can use the root producer in your components
Roact.mount(
<ReflexProvider producer={producer}>
<App />
</ReflexProvider>,
container,
);

My component won't stop dispatching actions

If your component is dispatching actions in a loop, it's likely that your action is running within the body of the component, or it indirectly triggers itself. This can become an issue if components that depend on the state are re-rendered every time the action is dispatched:

function Button() {
const { increment } = useProducer<RootProducer>();
const counter = useSelector((state) => state.counter);

// 🔴 This action updates counter and causes a re-render loop
increment(1);
}

To fix this, you can use useEffect to dispatch the action when a specific dependency changes:

function Button() {
const { increment } = useProducer<RootProducer>();
const counter = useSelector((state) => state.counter);

// ✅ This action is dispatched once when the component mounts
useEffect(() => {
increment(1);
}, []);
}