Braswelljr Documentation
website·Indexed 30 days ago
https://braswelljr.vercel.appA collection of technical guides and tutorials covering React (Context, Error Boundaries, createPortal, GSAP, Framer Motion), Go (Context, Error Handling, Goroutines, Channels), TypeScript Generics, and web technologies including HTTP/1.1, HTTP/2, HTTP/3, WebSockets, and Server-Sent Events.
What's inside Braswelljr
- Context is a React feature used to share state between components without manually passing props through every level of the component tree. This avoids 'Prop Drilling', which occurs when props are passed from a parent to a child, then to a grandchild, and so on. Context is intended for sharing state between closely related components (like siblings or cousins) and is not a direct replacement for global state management libraries like Redux.
Understand React Error Boundary capabilities and limitations
Error Boundaries are components that catch JavaScript errors in their child component tree, log them, and display a fallback UI. They only catch errors in components below them in the tree; they cannot catch errors thrown within themselves.
What they catch:
- Errors during rendering
- Errors in lifecycle methods
- Errors in constructors
What they DO NOT catch:
- Event handlers (use try/catch instead)
- Asynchronous code (e.g.,
setTimeout, promise callbacks) - Server-side rendering
- Errors thrown inside the error boundary component itself
Use the useGSAP hook for React animations
useGSAPfrom@gsap/reactis the idiomatic way to run GSAP in React. It wrapsuseLayoutEffectto provide a GSAP context scoped to a container ref, ensuring that all tweens and ScrollTriggers created within it are automatically killed when the component unmounts. It is specifically designed to handle React 18 Strict Mode's double-invocation.import { useRef } from 'react'; import { useGSAP } from '@gsap/react'; import { gsap } from 'gsap'; export function MyComponent() { const containerRef = useRef<HTMLDivElement>(null); useGSAP( () => { gsap.to('.box', { opacity: 0 }); }, { scope: containerRef } // Scopes all class selectors to this element ); return <div ref={containerRef}><div className="box">Hello</div></div>; }Create custom error types
To provide more structured error data (like field names or error codes), define a custom struct and implement theError() stringmethod. Use type assertion to access the custom fields of the error.type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed for field '%s': %s", e.Field, e.Message) } // Usage and Type Assertion if validationErr, ok := err.(*ValidationError); ok { fmt.Printf("Field: %s, Message: %s\n", validationErr.Field, validationErr.Message) }Create sequenced animations with gsap.timeline
Usegsap.timeline()to create a sequence of animations where tweens trigger one after another. You can control the exact start time of each tween using the position parameter to create overlaps or absolute timing.const tl = gsap.timeline(); tl.from('#avatar', { opacity: 0, scale: 0.5, duration: 0.4 }) // starts 0.1s before the previous tween ends .from('#name', { opacity: 0, x: -20 }, '-=0.1') // starts 0.15s before the previous tween ends .from('#role', { opacity: 0, x: -20 }, '-=0.15') // starts at absolute position 0.8s into the timeline .from('#badge', { opacity: 0, y: 10 }, '0.8') // starts immediately after a label called "reveal" .from('#cta', { opacity: 0 }, 'reveal')Install dependencies for motion/react tab animation
To set up a project with animated tabs using motion/react and Tailwind CSS v4, install the following dependencies. This includes the animation library, Tailwind CSS v4 (next), PostCSS plugins, and theclsxutility for dynamic class merging.# Using npm npm install motion/react tailwindcss@next @tailwindcss/postcss postcss autoprefixer clsx # Using pnpm pnpm add motion/react tailwindcss@next @tailwindcss/postcss postcss autoprefixer clsx # Using yarn yarn add motion/react tailwindcss@next @tailwindcss/postcss autoprefixer clsx # Using bun bun add motion/react tailwindcss@next @tailwindcss/postcss postcss autoprefixer clsxApply best practices for TypeScript Generics
Follow these guidelines to maintain clean and type-safe generic code:
- Prefer functional patterns: Use factory functions and composition instead of class inheritance.
- Use descriptive type parameters: Use
Tfor simple cases, but prefer names likeTKey,TValue, orTComponentfor clarity in complex generics. - Apply constraints: Use the
extendskeyword to prevent generics from being too permissive and to enforce required properties. - Provide default types: Use the
=syntax in generic declarations to provide a fallback type if one isn't specified. - Avoid over-engineering: Only use generics when genuine type flexibility is required.
// Example of a default type for a generic interface Box<T = string> { contents: T; } const defaultBox: Box = { contents: 'hello' }; // T defaults to stringStrategically place Error Boundaries in the component tree
Error boundaries can be placed at different levels of the component tree to isolate failures. Placing them around specific high-risk components (like sidebars, headers, or third-party widgets) ensures that a crash in one section does not bring down the entire application.function App() { return ( <div> <ErrorBoundary fallback={<div>Header Error</div>}> <Header /> </ErrorBoundary> <div style={{ display: 'flex' }}> <ErrorBoundary fallback={<div>Sidebar Error</div>}> <Sidebar /> </ErrorBoundary> <ErrorBoundary fallback={<div>Main Content Error</div>}> <MainContent /> </ErrorBoundary> </div> </div> ); }Implement basic error handling in functions
The standard pattern in Go is to return the error as the last return value of a function. The caller should explicitly check if the error is notnilbefore proceeding with the result.func divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil } func main() { result, err := divide(10, 0) if err != nil { fmt.Println("Error:", err) return } fmt.Println("Result:", result) }Choose between custom class boundaries and `react-error-boundary` library
Decide on an implementation strategy based on your project needs:
Use a custom class component when:
- You require zero external dependencies.
- You need full granular control over the boundary's behavior.
Use the
react-error-boundarylibrary when:- You prefer a functional API (using
FallbackComponent,onError, andonReset). - You need built-in reset support or the
useErrorBoundaryhook for handling asynchronous errors.
Important Constraints:
- There is no built-in Error Boundary component provided by React (including React 19).
- The boundary implementation must always be a class component (or a library wrapping one).
- Error boundaries only catch errors in rendering, lifecycle methods, and constructors. They do not catch errors in event handlers or asynchronous code; use
try/catchfor those cases.
Handle asynchronous errors in React
Error boundaries do not catch errors in asynchronous code (e.g.,fetchcalls). You must wrap async operations intry/catchblocks and manage the error state manually within the component to display a fallback UI.function AsyncComponent() { const [error, setError] = React.useState<Error | null>(null); const [data, setData] = React.useState<string | null>(null); const fetchData = async () => { try { const response = await fetch('/api/data'); if (!response.ok) throw new Error('Failed to fetch data'); const result = await response.json(); setData(result); } catch (err) { setError(err as Error); } }; if (error) return <div>Error: {error.message}</div>; return ( <div> <button onClick={fetchData}>Fetch Data</button> {data && <div>{data}</div>} </div> ); }Implement type-safe property access with keyof constraints
By combining multiple type parameters and thekeyofoperator, you can create functions that ensure a key exists on a given object and return the correct type for that specific property.function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user = { name: 'Alice', age: 30 }; const name = getProperty(user, 'name'); // type is string const age = getProperty(user, 'age'); // type is number