UNPKG

woby

Version:

A high-performance framework with fine-grained observable/signal-based reactivity for building rich applications.

416 lines (343 loc) 11.7 kB
# React to Woby Type Conversion Guide This document provides a comprehensive mapping of React types to their Woby equivalents, helping developers transition from React to Woby or understand how to work with both frameworks. ## Core Component and Element Types | React Type | Woby Equivalent | Description | |------------|-----------------|-------------| | `React.ReactNode` | `JSX.Child` | Represents any renderable content | | `React.FC<Props>` | `JSX.ComponentFunction<Props>` | Function component type | | `React.Component` | N/A (Woby uses functional components) | Class components aren't used in Woby | | `React.ComponentType<Props>` | `JSX.Component<Props>` | Union of function components and intrinsic elements | | `React.PropsWithChildren<P>` | `P & { children?: JSX.Child }` | Props interface with children | | `React.PropsWithoutRef<P>` | `P` | Props without ref (refs handled differently in Woby) | ## Hook Equivalents | React Hook | Woby Equivalent | Notes | |------------|-----------------|-------| | `useState` | `$` (observable creator) | Woby uses observables instead of state setters | | `useEffect` | `useEffect` | Automatic dependency tracking, no dependency array needed | | `useMemo` | `useMemo` | Automatic dependency tracking, no dependency array needed | | `useCallback` | Not needed | Functions are automatically optimized | | `useRef` | Ref callbacks or `$<T>()` | Direct DOM access or observable refs | | `useContext` | `useContext` | Similar API but with Woby's context system | ## Ref Handling | React Pattern | Woby Equivalent | Description | |---------------|-----------------|-------------| | `useRef<T>(null)` | `$<T>()` | Observable ref for DOM access | | `ref.current` | `$$()` | Accessing ref value | | `MutableRefObject<T>` | `JSX.Ref<T>` | Ref type definition | ## Context Types | React Type | Woby Equivalent | Description | |------------|-----------------|-------------| | `React.Context<T>` | `Woby.Context<T>` | Context object type | | `React.ContextType<T>` | Inferred from `useContext` | Context value type | | `React.Provider<T>` | `Context.Provider` | Context provider component | ## Event Types | React Type | Woby Equivalent | Description | |------------|-----------------|-------------| | `React.MouseEvent<T>` | `JSX.TargetedMouseEvent<T>` | Mouse event type | | `React.KeyboardEvent<T>` | `JSX.TargetedKeyboardEvent<T>` | Keyboard event type | | `React.ChangeEvent<T>` | `JSX.TargetedChangeEvent<T>` | Change event type | | `React.FormEvent<T>` | `JSX.TargetedEvent<T>` | Form event type | | `React.FocusEvent<T>` | `JSX.TargetedFocusEvent<T>` | Focus event type | ## HTML and DOM Types | React Type | Woby Equivalent | Description | |------------|-----------------|-------------| | `React.HTMLProps<T>` | `JSX.HTMLAttributes<T>` | HTML element props | | `React.HTMLAttributes<T>` | `JSX.HTMLAttributes<T>` | HTML attributes | | `React.CSSProperties` | `JSX.CSSProperties` | CSS properties object | | `React.ReactNodeArray` | `JSX.Child[]` | Array of renderable nodes | | `React.ReactPortal` | `JSX.Portal` | Portal component | ## HTML Utility Types Woby provides a set of HTML utility types for handling common HTML attribute patterns: | Woby Utility | Description | |--------------|-------------| | `HtmlBoolean` | Handles boolean values with automatic conversion | | `HtmlNumber` | Handles numeric values with automatic conversion | | `HtmlDate` | Handles Date values with ISO string serialization | | `HtmlBigInt` | Handles BigInt values with automatic conversion | | `HtmlObject` | Handles Object values with JSON serialization | | `HtmlLength` | Handles CSS length values (px, em, rem, %, etc.) | | `HtmlBox` | Handles CSS box values (margin, padding, border, etc.) | | `HtmlColor` | Handles CSS color values (hex, rgb, etc.) | | `HtmlStyle` | Handles CSS style values (objects and strings) | ### Usage Example ``tsx // Woby with HTML utilities import { $, HtmlBoolean, HtmlNumber, HtmlColor } from 'woby' interface Props { enabled?: boolean count?: number color?: string } const def = () => ({ enabled: $(false, HtmlBoolean), count: $(0, HtmlNumber), color: $('#000000', HtmlColor) }) ``` ## State and Dispatch Types | React Type | Woby Equivalent | Description | |------------|-----------------|-------------| | `React.Dispatch<A>` | Observable setter pattern | State update function | | `React.SetStateAction<S>` | `(prev: S) => S` or `S` | State update value or function | ## Component Definition Patterns ### React Function Component ```tsx // React interface Props { name: string; age?: number; } const MyComponent: React.FC<Props> = ({ name, age = 0, children }) => { return ( <div> <h1>Hello {name}</h1> <p>Age: {age}</p> {children} </div> ); }; ``` ### Woby Function Component ```tsx // Woby interface Props { name: string; age?: number; children?: JSX.Child; } const MyComponent: JSX.ComponentFunction<Props> = ({ name, age = 0, children }) => { return ( <div> <h1>Hello {name}</h1> <p>Age: {age}</p> {children} </div> ); }; ``` ## State Management ### React State ```tsx // React const [count, setCount] = useState(0); const increment = () => setCount(count + 1); ``` ### Woby State ```tsx // Woby const count = $(0); const increment = () => count(count() + 1); // Or with automatic unwrapping in reactive contexts: const increment = () => count($$(count) + 1); ``` ## Effect Management ### React Effect ```tsx // React useEffect(() => { console.log('Count changed:', count); }, [count]); ``` ### Woby Effect ```tsx // Woby - automatic dependency tracking useEffect(() => { console.log('Count changed:', $$(count)); }); ``` ## Key Differences Summary 1. **Reactivity System**: Woby uses observables instead of state setters 2. **Dependency Tracking**: Woby automatically tracks dependencies, no arrays needed 3. **Refs**: Woby uses direct function refs or observable-based refs without `.current` 4. **Hooks Rules**: Woby hooks can be conditional or nested (no strict rules) 5. **Performance**: Woby provides fine-grained updates through observables 6. **Component Types**: Woby focuses on functional components, no class components ## Migration Tips 1. Replace `useState` with `$()` for observable state 2. Remove dependency arrays from `useEffect` and `useMemo` 3. Use `$$()` to access observable values in reactive contexts 4. Replace `useRef` with observable refs or direct ref callbacks 5. Use Woby's built-in control flow components (`If`, `For`, `Switch`) instead of JavaScript conditionals 6. Replace class components with functional components ## Example Migrations ### Simple Counter Component **React:** ```tsx import React, { useState } from 'react'; interface CounterProps { initialCount?: number; } const Counter: React.FC<CounterProps> = ({ initialCount = 0 }) => { const [count, setCount] = useState(initialCount); const increment = () => setCount(prev => prev + 1); const decrement = () => setCount(prev => prev - 1); return ( <div> <h2>Count: {count}</h2> <button onClick={increment}>+</button> <button onClick={decrement}>-</button> </div> ); }; export default Counter; ``` **Woby:** ```tsx import { $, $$ } from 'woby'; import type { JSX } from 'woby'; interface CounterProps { initialCount?: number; children?: JSX.Child; } const Counter: JSX.ComponentFunction<CounterProps> = ({ initialCount = 0, children }) => { const count = $(initialCount); const increment = () => count($$(count) + 1); const decrement = () => count($$(count) - 1); return ( <div> <h2>Count: {count}</h2> <button onClick={increment}>+</button> <button onClick={decrement}>-</button> {children} </div> ); }; export default Counter; ``` ### Todo List Component **React:** ```tsx import React, { useState } from 'react'; interface Todo { id: number; text: string; completed: boolean; } interface TodoListProps { initialTodos?: Todo[]; } const TodoList: React.FC<TodoListProps> = ({ initialTodos = [] }) => { const [todos, setTodos] = useState<Todo[]>(initialTodos); const [inputValue, setInputValue] = useState(''); const addTodo = () => { if (inputValue.trim()) { setTodos(prev => [ ...prev, { id: Date.now(), text: inputValue, completed: false } ]); setInputValue(''); } }; const toggleTodo = (id: number) => { setTodos(prev => prev.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo ) ); }; const removeTodo = (id: number) => { setTodos(prev => prev.filter(todo => todo.id !== id)); }; return ( <div> <input value={inputValue} onChange={e => setInputValue(e.target.value)} onKeyPress={e => e.key === 'Enter' && addTodo()} /> <button onClick={addTodo}>Add</button> <ul> {todos.map(todo => ( <li key={todo.id} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> <input type="checkbox" checked={todo.completed} onChange={() => toggleTodo(todo.id)} /> <span>{todo.text}</span> <button onClick={() => removeTodo(todo.id)}>Remove</button> </li> ))} </ul> </div> ); }; export default TodoList; ``` **Woby:** ```tsx import { $, $$, For } from 'woby'; import type { JSX } from 'woby'; interface Todo { id: number; text: string; completed: boolean; } interface TodoListProps { initialTodos?: Todo[]; children?: JSX.Child; } const TodoList: JSX.ComponentFunction<TodoListProps> = ({ initialTodos = [], children }) => { const todos = $(initialTodos); const inputValue = $(''); const addTodo = () => { const text = $$(inputValue).trim(); if (text) { todos(prev => [ ...prev, { id: Date.now(), text, completed: false } ]); inputValue(''); } }; const toggleTodo = (id: number) => { todos(prev => prev.map(todo => todo.id === id ? { ...todo, completed: !$$(todo.completed) } : todo ) ); }; const removeTodo = (id: number) => { todos(prev => prev.filter(todo => todo.id !== id)); }; return ( <div> <input value={inputValue} onInput={e => inputValue(e.target.value)} onKeyPress={e => e.key === 'Enter' && addTodo()} /> <button onClick={addTodo}>Add</button> <ul> <For values={todos}> {todo => ( <li style={{ textDecoration: () => $$(todo).completed ? 'line-through' : 'none' }}> <input type="checkbox" checked={() => $$(todo).completed} onChange={() => toggleTodo($$(todo).id)} /> <span>{$(todo).text}</span> <button onClick={() => removeTodo($$(todo).id)}>Remove</button> </li> )} </For> </ul> {children} </div> ); }; export default TodoList; ```