Skip to content

Instantly share code, notes, and snippets.

@ellemedit
Created October 27, 2024 02:28
Show Gist options
  • Save ellemedit/6dcee3c94ea33c299ad97b226e603ee3 to your computer and use it in GitHub Desktop.
Save ellemedit/6dcee3c94ea33c299ad97b226e603ee3 to your computer and use it in GitHub Desktop.
React 19 Refactor Prompt

React 19 Component Refactoring Guide

Import Optimization

Replace wildcard imports with specific named imports:

// ❌ Before
import * as React from "react";

// ✅ After
import { useState, useEffect, forwardRef } from "react";

Type Safety Improvements

Component Props

Convert type aliases to interfaces and use ComponentPropsWithRef:

// ❌ Before
type ButtonProps = React.HTMLAttributes<HTMLButtonElement> & {
  variant?: "primary" | "secondary";
};

// ✅ After
interface ButtonProps extends ComponentPropsWithRef<"button"> {
  variant?: "primary" | "secondary";
}

Multiple Extension Pattern

// ❌ Before
type Props = BaseProps &
  ExtraProps & {
    custom?: boolean;
  };

// ✅ After
interface Props extends BaseProps, ExtraProps {
  custom?: boolean;
}

Modern Component Patterns

Remove ForwardRef Usage

// ❌ Before
const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
  return <button ref={ref} {...props} />;
});
Button.displayName = "Button";

// ✅ After
function Button(props: ButtonProps) {
  return <button {...props} />;
}

Export Pattern

// ❌ Before
export { Button, Input, Select, someConstant };

// ✅ After
export const someConstant = 1;

export function Button() {
  /* ... */
}

export function Input() {
  /* ... */
}

export function Select() {
  /* ... */
}

Render Props Pattern

Replace unsafe polymorphic components (Slot pattern) with render props:

// ❌ Before - Unsafe Slot Pattern
function Button({ asChild, ...props }: { asChild?: boolean }) {
  const Comp = asChild ? Slot : "button";
  return <Comp {...props} />;
}

// ✅ After - Safe Render Props Pattern
import { RenderProp } from "~/shared/render-props";

interface ButtonProps {
  render?: RenderProp; // intentionally not type RenderProp<XXX> for polymorphic usage
  // other props...
}

function Button({
  render = (props) => <button {...props} />,
  ...props
}: ButtonProps) {
  return render(props);
}

Important Notes

  1. The RenderProp type from ~/shared/render-props.ts is intentionally not generic to support polymorphic usage
  2. Default render function should always be provided for better DX
  3. Avoid using React.cloneElement as it will be deprecated in future React versions

Type Import Guidelines

  • Use ComponentPropsWithRef<T> instead of ComponentProps<T> when spreading all props
  • Import types directly from 'react' instead of using namespace imports

This refactoring guide ensures better type safety, modern React patterns, and future-proof components for React 19.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment