Welcome, cosmic traveler! During every space mission, unexpected situations can occur - engine failure, loss of communication, or a collision with an asteroid. The same applies to React applications - errors are inevitable. The key to success is the ability to anticipate and handle them, so that the entire space station doesn't shut down because of one faulty module.
In the React universe, we can distinguish several main types of errors:
Errors that occur during component rendering. This is the most common type of error in React.
1function SpaceModule({ data }) {
2 // If data is null, calling .map() will cause an error
3 return (
4 <div>
5 {data.map(item => (
6 <p key={item.id}>{item.name}</p>
7 ))}
8 </div>
9 );
10}Errors that occur in event handler functions, such as
onClick, onChange, etc.1function LaunchButton() {
2 const handleLaunch = () => {
3 // This error will NOT crash the entire application
4 // Event handlers are handled differently than rendering errors
5 throw new Error('Launch sequence failed!');
6 };
7
8 return <button onClick={handleLaunch}>Start mission</button>;
9}Errors in asynchronous operations like fetch, setTimeout, or Promise.
1function FetchPlanetData() {
2 const [data, setData] = useState(null);
3 const [error, setError] = useState(null);
4
5 useEffect(() => {
6 fetch('/api/planets')
7 .then(res => {
8 if (!res.ok) throw new Error('Failed to fetch data');
9 return res.json();
10 })
11 .then(setData)
12 .catch(setError); // Must manually catch the error
13 }, []);
14
15 if (error) return <p>Error: {error.message}</p>;
16 if (!data) return <p>Loading...</p>;
17 return <p>Found {data.length} planets</p>;
18}In traditional JavaScript, we use
try/catch to catch errors. However, in React it has serious limitations.1function App() {
2 // try/catch will NOT catch a child's rendering error!
3 try {
4 return (
5 <div>
6 <BrokenComponent /> {/* Rendering error */}
7 </div>
8 );
9 } catch (error) {
10 // This block will NEVER be executed
11 // for errors in rendering child components
12 return <p>Error!</p>;
13 }
14}1function SafeButton() {
2 const handleClick = () => {
3 try {
4 // This will work - event handler is a regular function
5 riskyOperation();
6 } catch (error) {
7 console.error('Operation error:', error.message);
8 }
9 };
10
11 return <button onClick={handleClick}>Safe button</button>;
12}React renders components declaratively, not imperatively. When you write JSX, you're not executing code in place - you're creating a description of the UI that React will process later. That's why standard
try/catch cannot catch errors that arise during rendering of child components.It's like trying to catch a failure on a space station using a single sensor in the control center - you won't see a problem in a distant module until a catastrophe occurs. We need a system that monitors each module individually.
When a rendering error is not caught, React since version 16 unmounts the entire component tree by default. The user sees a blank page - the white screen of death.
1// Without error handling - one faulty component destroys the entire app
2function SpaceStation() {
3 return (
4 <div>
5 <Navigation /> {/* Works correctly */}
6 <BrokenModule /> {/* Throws an error! */}
7 <StatusPanel /> {/* Won't be displayed */}
8 </div>
9 );
10}
11// Result: the entire space station - emptiness, white screenIn the next lessons, we'll learn how to deal with these problems using Error Boundaries, React.lazy(), Suspense, and many other techniques!