React 18 allows components to render 'undefined'

Until React 18, if a component rendered undefined, React would throw an error at runtime.

//Shape.jsx

import React from 'react';
import Circle from './Circle';
import Square from './Square';

function Shape({type}) {
  if(type === 'circle') {
    return <Circle />
  }
  if(type === 'square') {
    return <Square />
  }
}
export default Shape;

//App.jsx

function App() : ComponentType {
  return(<Shape type="rectangle"/>)
}

//Rectangle is passed as props in type which is not handled in the Shape component, so it will throw an error

Error: Shape(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

To fix the error, we had to return null.

import React from 'react';
import Circle from './Circle';
import Square from './Square';

function Shape({type}) {
  if(type === 'circle') {
    return <Circle />
  }
  if(type === 'square') {
    return <Square />
  }
  return null;
}
export default Shape;

With the changes in React 18, no runtime error will be thrown if nothing is returned from the component.

Let us look into the reasons why React came up with this change.

Why does React allows components to render undefined?
  1. Instead of throwing a runtime error, linting would be a better choice

    Runtime error was added back in 2017 when type systems and linting were not very popular in the ecosystem. Now, linting has evolved a lot. We can use its powers to handle these types of errors.

    We cannot tell the difference between forgetting to return and explicitly returning undefined at runtime. As linting operates at the code syntax, it can determine this difference.

    Linting can also give a more specific error message pointing to the source of the error. This can help debug the issue more quickly than a generic message at runtime.

  2. Creating the correct type was hard

    Not able to create a generic type for something that React can render, which we can use in place of any prop or component. It has been a long-standing issue in the ecosystem.

    Consider we want to render the children prop in our Shapecomponent.

    
       //Shape.jsx 
    
       const Shape = ({children}: ComponentType): ComponentType => {
         return children;
       }
    
       //App.jsx
    
       function App() : ComponentType{
         return(<Shape />);
       }
    
    

    How can we manage to handle ComponentType as children and Shape? We cannot include undefined because it would throw an error at runtime. If we exclude undefined, then everywhere we pass the children down to Shape, we would need to check if children is undefined.

    This simplest solution was to allow ‘undefined’ to be rendered.

    To add further, for Server Components, it is important to have a general type for things React can render. This is because we may need to be able to accept children passed from Server Components.

  3. To maintain consistent behavior

    Recently changes were made to Suspense to handle null or undefined fallbacks. These changes allowed undefined fallbacks to render instead of throwing an error.

    To maintain consistency in React, it was necessary to allow components to render undefined.

Check out the PR and the write-up for more details.

Need help on your Ruby on Rails or React project?

Join Our Newsletter