メインコンテンツまでスキップ

「ref」タグの記事が2件件あります

全てのタグを見る

React Error Boundary

· 約2分
Mikyan
白い柴犬

Error boundaries are React components that catch Javascript errors, in their child component tree, log those errors, and display a fallback UI instead of crashing the entire app. Without them, an error in one component can bring down your whole application.

The word "boundary" emphasizes that these components create a containment zone for errors:

Error Boundaries are specifically for:

React component lifecycle errors Render method errors Constructor errors in child components

How error boundaries works

How React Rendering works

React rendering happens in two phases:

// 1. Render Phase - React calls your components
function MyComponent() {
// If error happens here...
return <div>{undefined.toString()}</div>; // 💥 Crash!
}

// 2. Commit Phase - React updates the DOM
// (If render phase succeeds)

What Makes Component Errors Special

  1. They Happen Inside React's Control
// React controls this execution:
function BrokenComponent() {
const user = null;
return <h1>{user.name}</h1>; // React catches this error
}

// React DOESN'T control this:
<button onClick={() => {
throw new Error(); // Happens outside React's render cycle
}}>
  1. Synchronous Execution
// Error boundaries can catch this (synchronous):
function Component() {
throw new Error('Sync error');
return <div />;
}

// But NOT this (asynchronous):
function Component() {
setTimeout(() => {
throw new Error('Async error'); // Outside React's try/catch
}, 1000);
return <div />;
}

Why Error Boundaries work

Usage

✅ Use Error Boundaries For

  1. Unexpected Runtime Errors
  2. Third-Party Component Errors
  3. Dynamic/Complex Components

❌ DON'T Use Error Boundaries For

  1. Expected Errors (Handle These Explicitly)
  2. Event Handler Errors
  3. Async Errors

Best Practices

  1. Strategic Placement
function App() {
return (
<ErrorBoundary> {/* App-level boundary */}
<Header />
<ErrorBoundary> {/* Feature-level boundary */}
<RiskyFeature />
</ErrorBoundary>
<Footer />
</ErrorBoundary>
);
}
  1. Granular for Isolation
<div>
{widgets.map(widget => (
<ErrorBoundary key={widget.id}> {/* Each widget isolated */}
<Widget {...widget} />
</ErrorBoundary>
))}
</div>
  1. Not for Control Flow
// ❌ Bad: Using error boundary for expected cases
<ErrorBoundary fallback={<LoginForm />}>
<AuthenticatedApp />
</ErrorBoundary>

// ✅ Good: Explicit control flow
{isAuthenticated ? <AuthenticatedApp /> : <LoginForm />}

React 19 Ref

· 約3分
Mikyan
白い柴犬

In React 19, ref can directly be passed to a component as a property, and forwardRef become unrecommanded.

About ref

In React, a ref (reference) let you operate lower-level imperative API on the following elements:

  • DOM nodes
  • Class component instances

Usually it is created with useRef hook.

function Chat() {
const endRef = useRef();
useEffect(() => {
endRef.current.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<div className="chat-window">
{messages.map(m => <Message key={m.id} />)}
<div ref={endRef} />
</div>
);
}

Sometimes we need to pass ref down to point to one component of a child component, below are suitable situations:

  • When you build a HOC component
  • When you wrapping a core component with customized design (like wrapping a material UI Input)

Note that usually it is not a good practice, since it increase the complicity and voilate the capsluating philosoph, we should be careful about the usecases.

Improved ref API in React.19

Before React 19, it is not allowed to pass ref as a property, Instead, forwardRef is needed:

import React, { forwardRef, useRef, useImperativeHandle } from 'react';

const FancyInput = forwardRef(function FancyInput(_, ref) {
const inputRef = useRef();

useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => { if (inputRef.current) inputRef.current.value = ''; }
}), []);

return <input ref={inputRef} className="border rounded p-2" />;
});

export default FancyInput;

// Parent.jsx
import React, { useRef } from 'react';
import FancyInput from './FancyInput';

function Parent() {
const fancyRef = useRef();

return (
<>
<FancyInput ref={fancyRef} />
<button onClick={() => fancyRef.current.focus()}>Focus</button>
<button onClick={() => fancyRef.current.clear()}>Clear</button>
</>
);
}

Note in the example, useImperativeHandle is used to defines the customer handler of ref. By declaring useImperativeHandle in the component, we can:

  • rewrite the ref handler behavior
  • safely limit the handlers of the ref

By using useImperativeHandle, the original ref donot directly pass to the input, instead in the component we define a new ref, to let the ref operates indirectly.

However, from React 19, forwardRef is not recommended, and ref can be passed directly, which is more concise and have better readability:

import React, { forwardRef, useRef, useImperativeHandle } from 'react';

function FancyInput({ ref }) {
const inputRef = useRef();

useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => { if (inputRef.current) inputRef.current.value = ''; }
}), []);

return <input ref={inputRef} className="border rounded p-2" />;
});

export default FancyInput;

// Parent.jsx
import React, { useRef } from 'react';
import FancyInput from './FancyInput';

function Parent() {
const fancyRef = useRef();

return (
<>
<FancyInput ref={fancyRef} />
<button onClick={() => fancyRef.current.focus()}>Focus</button>
<button onClick={() => fancyRef.current.clear()}>Clear</button>
</>
);
}