React API Reference
This document provides usage instructions and examples for key APIs in React, with a focus on those related to performance optimization.
createContext
createContext
is used to create a context, allowing components in a component tree to share data without having to pass props down through multiple levels.
Basic Usage
import React, { hooks, createContext } from "@dao3fun/react";
const { useContext, useState } = hooks;
// Create a context
const ThemeContext = createContext("light");
// A child component accesses the context via useContext
function ThemedButton() {
// Use the value from the context
const theme = useContext(ThemeContext);
return (
<box
style={{
backgroundColor:
theme === "dark"
? Vec3.create({ r: 50, g: 50, b: 50 })
: Vec3.create({ r: 240, g: 240, b: 240 }),
size: { offset: Vec2.create({ x: 120, y: 40 }) },
}}
>
<text
style={{
textColor:
theme === "dark"
? Vec3.create({ r: 255, g: 255, b: 255 })
: Vec3.create({ r: 50, g: 50, b: 50 }),
}}
>
Current theme: {theme}
</text>
</box>
);
}
// An intermediate component that doesn't need to know about the theme
function Toolbar() {
return (
<box>
<ThemedButton />
</box>
);
}
// The root component provides the context value
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={theme}>
<box>
<Toolbar />
<box
style={{
backgroundColor: Vec3.create({ r: 0, g: 122, b: 255 }),
size: { offset: Vec2.create({ x: 150, y: 40 }) },
position: { offset: Vec2.create({ x: 0, y: 50 }) },
}}
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<text style={{ textColor: Vec3.create({ r: 255, g: 255, b: 255 }) }}>
Switch Theme
</text>
</box>
</box>
</ThemeContext.Provider>
);
}
Providing a Default Value
You can provide a default value when creating a context, which will be used if there is no matching Provider in the component tree:
// Set the default theme to "light"
const ThemeContext = createContext("light");
Nested Providers
Context Providers can be nested, and an inner Provider will override the value of an outer one:
function NestedProviders() {
return (
<ThemeContext.Provider value="dark">
<box>
{/* Components here will receive the "dark" theme */}
<ThemedButton />
<ThemeContext.Provider value="light">
{/* Components here will receive the "light" theme */}
<ThemedButton />
</ThemeContext.Provider>
</box>
</ThemeContext.Provider>
);
}
Best Practices
- Avoid Overuse: Only use it for data that truly needs to be shared across multiple levels of the component tree.
- Split Contexts: Use different Contexts for different types of data.
- Consider Performance: Changes to a Context value will cause all consuming components to re-render.
forwardRef
forwardRef
allows a component to receive a ref passed from a parent and forward it to a DOM element or component instance within the child.
Basic Usage
import React, { hooks, forwardRef } from "@dao3fun/react";
const { useRef } = hooks;
// Create a component that can forward a ref using forwardRef
const CustomInput = forwardRef((props, ref) => {
return (
<input
ref={ref}
style={{
size: { offset: Vec2.create({ x: 200, y: 40 }) },
backgroundColor: Vec3.create({ r: 245, g: 245, b: 245 }),
}}
onBlur={(e) => props.onUpdate && props.onUpdate(e.target.textContent)}
>
{props.defaultValue || ""}
</input>
);
});
// Use the component with a ref
function Form() {
const inputRef = useRef(null);
const focusInput = () => {
// You can directly access the input element
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<box>
<CustomInput ref={inputRef} defaultValue="Please enter content" />
<box
style={{
backgroundColor: Vec3.create({ r: 0, g: 122, b: 255 }),
size: { offset: Vec2.create({ x: 120, y: 40 }) },
position: { offset: Vec2.create({ x: 0, y: 50 }) },
}}
onClick={focusInput}
>
<text style={{ textColor: Vec3.create({ r: 255, g: 255, b: 255 }) }}>
Focus Input
</text>
</box>
</box>
);
}
Combining with useImperativeHandle
Combining with useImperativeHandle
allows you to customize the instance value exposed to the parent component:
import React, { hooks, forwardRef } from "@dao3fun/react";
const { useImperativeHandle, useRef, useState } = hooks;
// Define the component instance interface
interface InputHandles {
focus: () => void;
clear: () => void;
getValue: () => string;
}
// Create an input with custom actions
const CustomInput = forwardRef<InputHandles>((props, ref) => {
const [value, setValue] = useState(props.defaultValue || "");
const inputRef = useRef(null);
// Customize the exposed methods
useImperativeHandle(
ref,
() => ({
focus: () => {
inputRef.current?.focus();
},
clear: () => {
setValue("");
},
getValue: () => {
return value;
},
}),
[value]
);
return (
<input
ref={inputRef}
style={{
size: { offset: Vec2.create({ x: 200, y: 40 }) },
backgroundColor: Vec3.create({ r: 245, g: 245, b: 245 }),
}}
onBlur={(e) => setValue(e.target.textContent)}
>
{value}
</input>
);
});
// Use the component with enhanced functionality
function EnhancedForm() {
const inputRef = useRef<InputHandles>(null);
const handleClear = () => {
inputRef.current?.clear();
};
const handleGetValue = () => {
const value = inputRef.current?.getValue() || "";
console.log("Current value:", value);
};
return (
<box>
<CustomInput ref={inputRef} defaultValue="Please enter content" />
<box style={{ position: { offset: Vec2.create({ x: 0, y: 50 }) } }}>
<box
style={{
backgroundColor: Vec3.create({ r: 255, g: 59, b: 48 }),
size: { offset: Vec2.create({ x: 100, y: 40 }) },
}}
onClick={handleClear}
>
<text style={{ textColor: Vec3.create({ r: 255, g: 255, b: 255 }) }}>
Clear
</text>
</box>
<box
style={{
backgroundColor: Vec3.create({ r: 76, g: 217, b: 100 }),
size: { offset: Vec2.create({ x: 100, y: 40 }) },
position: { offset: Vec2.create({ x: 110, y: 0 }) },
}}
onClick={handleGetValue}
>
<text style={{ textColor: Vec3.create({ r: 255, g: 255, b: 255 }) }}>
Get Value
</text>
</box>
</box>
</box>
);
}
memo
memo
is a higher-order component that memoizes the rendered output of a component, preventing re-renders if its props have not changed.
Basic Usage
import React, { hooks, memo } from "@dao3fun/react";
const { useState } = hooks;
// A memoized component
const UserProfile = memo(function UserProfile({ name, age }) {
console.log(`Rendering UserProfile for ${name}`);
return (
<box>
<text>Name: {name}</text>
<text>Age: {age}</text>
</box>
);
});
function App() {
const [user, setUser] = useState({ name: "Alice", age: 30 });
const [count, setCount] = useState(0);
return (
<box>
<UserProfile name={user.name} age={user.age} />
<text>Count: {count}</text>
<box onClick={() => setCount(count + 1)}>
<text>Increment Count</text>
</box>
<box onClick={() => setUser({ name: "Alice", age: 30 })}>
<text>Set Same User</text>
</box>
</box>
);
}
When you click "Increment Count", the App
component re-renders, but since the props for UserProfile
(name
and age
) have not changed, UserProfile
will not re-render.
Custom Comparison Function
You can provide a custom comparison function to memo
to control when the component should re-render.
function arePropsEqual(prevProps, nextProps) {
// Only re-render if the user's name changes
return prevProps.user.name === nextProps.user.name;
}
const MemoizedComponent = memo(MyComponent, arePropsEqual);
Best Practices
- Use for Pure Components: Best for components that render the same output for the same props.
- Avoid with Frequent Prop Changes: If props change frequently,
memo
can add unnecessary overhead. - Combine with
useCallback
anduseMemo
: To prevent re-renders caused by functions or objects being re-created.
lazy
lazy
allows you to define a component that is loaded lazily (on demand). This is useful for code-splitting and improving initial load times.
Basic Usage
import React, { Suspense, lazy } from "@dao3fun/react";
// Lazily load a component
const OtherComponent = lazy(() => import("./OtherComponent"));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
Error Boundaries
You can use an error boundary to handle errors that occur during lazy loading.
import { ErrorBoundary } from "./ErrorBoundary";
function App() {
return (
<div>
<h1>My App</h1>
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</ErrorBoundary>
</div>
);
}
startTransition
startTransition
lets you mark a state update as a "transition," which tells React that it can be interrupted by more urgent updates. This is useful for keeping the UI responsive during heavy rendering tasks.
Basic Usage
import React, { hooks } from "@dao3fun/react";
const { useState, useTransition } = hooks;
function App() {
const [isPending, startTransition] = useTransition();
const [text, setText] = useState("");
const [items, setItems] = useState([]);
function handleChange(e) {
setText(e.target.value);
startTransition(() => {
// This is a slow operation
const newItems = Array.from({ length: 20000 }, (_, index) => {
return `Item ${e.target.value} ${index}`;
});
setItems(newItems);
});
}
return (
<div>
<input type="text" value={text} onChange={handleChange} />
{isPending ? (
"Loading..."
) : (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
)}
</div>
);
}
In this example, the input field remains responsive even while the large list is being rendered because the state update for items
is wrapped in startTransition
.