A universal React-compatible render engine for building applications across multiple platforms
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Complete React hooks implementation for state management and side effects in functional components. All hooks are compatible with React hooks and follow the same rules and behavior patterns.
Manages local state in functional components. Returns current state value and setter function.
/**
* Manages local state in functional components
* @param initialState - Initial state value or function returning initial state
* @returns Array containing [currentState, setStateFunction]
*/
function useState(initialState);Usage Examples:
import { createElement, useState } from 'rax';
function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// With function initializer (for expensive computations)
const [expensiveValue, setExpensiveValue] = useState(() => {
return computeExpensiveValue();
});
const increment = () => setCount(count + 1);
const decrement = () => setCount(prevCount => prevCount - 1);
return createElement('div', null,
createElement('p', null, `Count: ${count}`),
createElement('input', {
value: name,
onChange: (e) => setName(e.target.value),
placeholder: 'Enter name'
}),
createElement('button', { onClick: increment }, '+'),
createElement('button', { onClick: decrement }, '-')
);
}Performs side effects in functional components. Equivalent to componentDidMount, componentDidUpdate, and componentWillUnmount combined.
/**
* Performs side effects in functional components (deferred execution)
* @param effect - Effect function, can return cleanup function
* @param inputs - Optional dependency array for effect optimization
*/
function useEffect(effect, inputs);Usage Examples:
import { createElement, useState, useEffect } from 'rax';
function DataFetcher({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// Effect with cleanup
useEffect(() => {
let cancelled = false;
async function fetchUser() {
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
if (!cancelled) {
setUser(userData);
setLoading(false);
}
} catch (error) {
if (!cancelled) {
console.error('Failed to fetch user:', error);
setLoading(false);
}
}
}
fetchUser();
// Cleanup function
return () => {
cancelled = true;
};
}, [userId]); // Re-run when userId changes
// Effect without dependencies (runs after every render)
useEffect(() => {
document.title = user ? `User: ${user.name}` : 'Loading...';
});
// Effect with empty dependencies (runs once on mount)
useEffect(() => {
console.log('Component mounted');
return () => console.log('Component unmounting');
}, []);
if (loading) {
return createElement('div', null, 'Loading user...');
}
return createElement('div', null,
createElement('h1', null, user.name),
createElement('p', null, user.email)
);
}Synchronous version of useEffect that fires before the browser paints. Use for DOM measurements and synchronous DOM mutations.
/**
* Synchronous version of useEffect that fires before browser paint
* @param effect - Effect function, can return cleanup function
* @param inputs - Optional dependency array for effect optimization
*/
function useLayoutEffect(effect, inputs);Consumes context values from the nearest Provider component.
/**
* Consumes context value from nearest Provider
* @param context - Context object created by createContext
* @returns Current context value
*/
function useContext(context);Usage Examples:
import { createElement, createContext, useContext, useState } from 'rax';
// Create context
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
return createElement(ThemeContext.Provider, { value: theme },
createElement('div', null,
createElement(ThemeToggle, { onToggle: () => setTheme(theme === 'light' ? 'dark' : 'light') }),
createElement(ThemedButton)
)
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return createElement('button', {
style: {
backgroundColor: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#333' : '#fff'
}
}, `I'm ${theme} themed!`);
}Creates a mutable ref object that persists across renders.
/**
* Creates a mutable ref object that persists across renders
* @param initialValue - Initial value for the ref
* @returns RefObject with current property
*/
function useRef(initialValue);Usage Examples:
import { createElement, useRef, useEffect } from 'rax';
function FocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// Focus input on mount
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
const focusInput = () => {
if (inputRef.current) {
inputRef.current.focus();
}
};
return createElement('div', null,
createElement('input', { ref: inputRef, type: 'text' }),
createElement('button', { onClick: focusInput }, 'Focus Input')
);
}Memoizes callback functions to prevent unnecessary re-renders of child components.
/**
* Memoizes callback functions based on dependencies
* @param callback - Function to memoize
* @param inputs - Dependency array for memoization
* @returns Memoized callback function
*/
function useCallback(callback, inputs);Memoizes computed values to avoid expensive calculations on every render.
/**
* Memoizes computed values based on dependencies
* @param create - Function that returns the value to memoize
* @param inputs - Dependency array for memoization
* @returns Memoized value
*/
function useMemo(create, inputs);Usage Examples:
import { createElement, useState, useCallback, useMemo } from 'rax';
function ExpensiveCalculator({ items }) {
const [filter, setFilter] = useState('');
// Memoize expensive calculation
const expensiveValue = useMemo(() => {
console.log('Calculating expensive value...');
return items.reduce((sum, item) => sum + item.value, 0);
}, [items]);
// Memoize callback to prevent child re-renders
const handleFilterChange = useCallback((e) => {
setFilter(e.target.value);
}, []);
const filteredItems = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
return createElement('div', null,
createElement('input', {
value: filter,
onChange: handleFilterChange,
placeholder: 'Filter items...'
}),
createElement('p', null, `Total value: ${expensiveValue}`),
createElement('p', null, `Filtered items: ${filteredItems.length}`)
);
}Manages complex state logic with a reducer function, similar to Redux.
/**
* Manages state with a reducer function
* @param reducer - Reducer function (state, action) => newState
* @param initialArg - Initial state or argument for init function
* @param init - Optional function to compute initial state
* @returns Array containing [currentState, dispatchFunction]
*/
function useReducer(reducer, initialArg, init);Usage Examples:
import { createElement, useReducer } from 'rax';
// Reducer function
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return createElement('div', null,
createElement('p', null, `Count: ${state.count}`),
createElement('button', {
onClick: () => dispatch({ type: 'increment' })
}, '+'),
createElement('button', {
onClick: () => dispatch({ type: 'decrement' })
}, '-'),
createElement('button', {
onClick: () => dispatch({ type: 'reset' })
}, 'Reset')
);
}Customizes the instance value exposed by a ref when using forwardRef.
/**
* Customizes the instance value exposed by a ref
* @param ref - Ref object to customize
* @param create - Function that returns the instance value
* @param inputs - Optional dependency array
*/
function useImperativeHandle(ref, create, inputs);Usage Examples:
import { createElement, forwardRef, useImperativeHandle, useRef } from 'rax';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
getValue() {
return inputRef.current.value;
}
}), []);
return createElement('input', { ref: inputRef, ...props });
});
function App() {
const fancyInputRef = useRef();
const handleFocus = () => {
fancyInputRef.current.focus();
};
const handleGetValue = () => {
alert(fancyInputRef.current.getValue());
};
return createElement('div', null,
createElement(FancyInput, { ref: fancyInputRef }),
createElement('button', { onClick: handleFocus }, 'Focus'),
createElement('button', { onClick: handleGetValue }, 'Get Value')
);
}All Rax hooks follow the same rules as React hooks:
// State hook types
type StateUpdater<S> = (prevState: S) => S;
type SetStateAction<S> = S | StateUpdater<S>;
type StateSetter<S> = (value: SetStateAction<S>) => void;
// Effect hook types
type EffectCallback = () => (void | (() => void));
type DependencyList = ReadonlyArray<any>;
// Reducer hook types
type Reducer<S, A> = (prevState: S, action: A) => S;
type ReducerState<R> = R extends Reducer<infer S, any> ? S : never;
type ReducerAction<R> = R extends Reducer<any, infer A> ? A : never;
type Dispatch<A> = (value: A) => void;
// Ref hook types
interface MutableRefObject<T> {
current: T;
}
// Callback and memo hook types
type CallbackFunction = (...args: any[]) => any;
type MemoFactory<T> = () => T;
// Context hook types
interface Context<T> {
Provider: ComponentType<{ value: T; children?: any }>;
Consumer: ComponentType<{ children: (value: T) => any }>;
_contextID: string;
_defaultValue: T;
}Install with Tessl CLI
npx tessl i tessl/npm-rax