Custom Component React API
Your Custom Component will be rendered by Superblocks. You will use the provided Custom Components React API to communicate the internal component state to the Superblocks Application, and to trigger events. Like other Superblocks components, your component will re-render when its properties are modified.
When the component is rendered, it will receive a props object consisting of each property specified in the config.ts
file.
Note: You can use either React 17 or 18 in Superblocks, which is detected automatically by the CLI.
Properties
Properties represent the state of the component in Superblocks.
In your React code, you will access each property by the path
specified in the config.ts
file. You will manipulate these properties from your React code to set the component state in Superblocks.
Each property’s value is evaluated in Superblocks, and the component receives that evaluated value. For example, if the application supplies {{API1.response}}
as the value for a property, the component will receive the evaluated value for that property.
Updating Properties
In order to update the Superblocks component state, you will leverage the updateProperties method. You will use this method to communicate your internal component state to the Superblocks Application. This method is provided via the useSuperblocksContext()
hook.
This method accepts a partial of the component properties and their values. Any properties not provided in the object will not be updated in Superblocks.
updateProperties: (props: Partial<Props>) => void;
Calling this method will cause the component to rerender, and will also notify any other components in your Application which depend on the updated properties, so that they also rerender.
Event Triggers
In your config.ts
file, you define the list of Events which your component exposes. In your Superblocks Application, you can configure event handlers for these events in the relevant section of the component's properties panel.
For each defined event, you are provided an Event Trigger, which is a method to trigger the relevant event in Superblocks. The name of the relevant event trigger matches the path property for the corresponding event in your config.ts
file. Each event trigger is a function which expects no arguments.
These event triggers are also provided via the useSuperblocksContext()
hook hook, specifically via the events
object returned by that hook.
Hooks
Superblocks provides a few different React Hooks to interface with your Superblocks App.
useSuperblocksContext
The useSuperblocksContext<Props, EventTriggers>()
hook returns
- the
updateProperties
method, which is used to update each component property's value in the Superblocks app state - an events object which contains a method for each Event Trigger. These methods can be called to trigger the corresponding event in Superblocks
useSuperblocksIsLoading
The useSuperblocksIsLoading()
hook returns a boolean indicating whether any data the component is dependent upon is currently loading, a case which you will want to handle gracefully so your component does not crash & the state is clearly conveyed to users. If you use this hook, your component will re-render each time this value changes.
useSuperblocksWidgetSize
The useSuperblocksWidgetSize()
hook returns an object containing the component's dimensions in the Superblocks App.
These dimensions will be useful if your component display depends on the size, for example, to support scrolling or a responsive layout. If you use this hook, your component will re-render each time the component dimensions change.
Example - Building a Counter
In order to build Custom Components in Superblocks, it is crucial to understand the internal component state and the Superblocks Application state, and account for both. To illustrate these concepts, let's walk through a simple example of building a Counter component with an increment button.
Let's start with some non-Superblocks-specific React code for the counter. In order to track the count, we need to create and maintain some state, counter
, within the component. We would modify this state when the user clicks to increment, causing the component to re-render with the appropriate new value.
import { useState } from 'react';
export default function Counter() {
const [counter, setCounter] = useState<number>(0);
const increment = () => {
setCounter(prevCount => prevCount + 1);
};
return (
<div className="container">
<p>Current count: {counter}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
The code above will render a counter as expected. However, the count will not be available in our Superblocks Application, since the state is only being maintained internally.
In order to integrate this component with the rest of a Superblocks application, we want to
- Integrate the internal state of the component with the Superblocks Application State, so that we can access the state throughout our application, such as in other components and APIs, to both read and set the state.
- Trigger Events, so that other parts of our Superblocks application can be notified when events occur
In order to achieve those goals, we'd first define a count
property and an onCountChange
event as part of our component (in the config.ts
file).
Then, we would use the Superblocks React API to maintain the state and fire the event. Our updated code would look something like this:
import React, { useState } from 'react';
import { useSuperblocksContext } from "@superblocksteam/custom-components";
import { type Props, type EventTriggers } from "./types";
export default function Counter({
count,
}: Props) {
// we no longer need to maintain the state internally
// since we will track it via Superblocks Application State
// const [counter, setCounter] = useState<number>(count);
// call useSuperblocksContext() to get the updateProperties method and event triggers
const {updateProperties, events: {onCountChange}} = useSuperblocksContext<Props, EventTriggers>();
const increment = () => {
// no need to update the internal state of the component
// setCounter(prevCount => prevCount + 1);
// use the provided updateProperties() method to set the *count* property
updateProperties({ count: count + 1 });
// trigger the onCountChange event
onCountChange();
};
// display the count property as the current count
return (
<div>
<p>Current count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
With the changes above, we will now:
- Display the
count
property as the Current count in our component - Update the
count
property when a user clicks to increment - Fire the
onCountChange
event when the user clicks to increment
Internal Component State vs. Superblocks State
In the example above, we replaced the internal state management to instead manage the component state entirely via Superblocks Application State. The advantage of this pattern is that it allows the rest of our Superblocks Application to accesss the component's state and hook into its events, using the component as we would any built-in Superblocks component!
In some cases however, you may not want or need to expose the internal state of the component to the Superblocks Application. For example, imagine, we wanted to add an input to the component, which allows the user to type in a value for the counter, click a button, and update the count to this provided value. A common pattern for building inputs in React is to control the input using React state, and we would want to leverage this pattern to build our input and ensure that when the user clicks the button to set the count, we properly set the count to the provided value. In this case, there is no need to expose the state of the input to the Superblocks Application, and thus we would want to manage that state entirely within the component's React code.
The example code below includes the input described. In summary, it is appropriate to manage some state internally within your component code; however, for any information you wish to access within the Superblocks Application, you will want to use Superblocks for state management via the Custom Components React API.
import React, { useState } from 'react';
import { useSuperblocksContext } from "@superblocksteam/custom-components";
import { type Props, type EventTriggers } from "./types";
export default function Component({
count,
}: Props) {
// call useSuperblocksContext() to get the updateProperties method and event triggers
const {updateProperties, events: {onCountChange}} = useSuperblocksContext<Props, EventTriggers>();
// track the state of the controlled input
const [counterInputValue, setCounterInputValue] = useState<number>(0);
const increment = () => {
// use the provided updateProperties() method to set the *count* property
updateProperties({ count: count + 1 });
// trigger the onCountChange event
onCountChange();
};
const handleSetCount = () => {
// use the provided updateProperties() method to set the *count* property to the counterInputValue
updateProperties({ count: counterInputValue });
// trigger the onCountChange event
onCountChange();
};
return (
<div className="container">
<p>Current count: {count}</p>
<button onClick={increment}>Increment</button>
<div className="set-count-input">
<input
type="number"
value={counterInputValue}
onChange={(event) =>
setCounterInputValue(parseInt(event.target.value))
}></input>
<button onClick={handleSetCount}>Set Count</button>
</div>
</div>
);
}