Content-Length: 660811 | pFad | http://github.com/typescript-cheatsheets/react/commit/25d30ac3361c231ef7fe7f7acfe63c29e021460f

94 React 19 modernization: ref as prop and improved structure (#789) · typescript-cheatsheets/react@25d30ac · GitHub
Skip to content

Commit 25d30ac

Browse files
React 19 modernization: ref as prop and improved structure (#789)
1 parent 0f2f10f commit 25d30ac

File tree

3 files changed

+189
-81
lines changed

3 files changed

+189
-81
lines changed

README.md

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,22 +1599,62 @@ When you don't know what to choose, prefer runtime checking and throwing over ty
15991599

16001600
#### forwardRef/createRef
16011601

1602-
Check the [Hooks section](https://github.com/typescript-cheatsheets/react/blob/main/README.md#hooks) for `useRef`.
1602+
For `useRef`, check the [Hooks section](/docs/basic/getting-started/hooks#useref).
16031603

1604-
`createRef`:
1604+
#### Ref as a Prop (Recommended for React 19+)
1605+
1606+
In React 19+, you can access `ref` directly as a prop in function components - no `forwardRef` wrapper needed.
1607+
1608+
##### Option 1: Inherit all props from a native element
1609+
1610+
Use `ComponentPropsWithRef` to inherit all props from a native element.
16051611

16061612
```tsx
1607-
import { createRef, PureComponent } from "react";
1613+
import { ComponentPropsWithRef, useRef } from "react";
16081614

1609-
class CssThemeProvider extends PureComponent<Props> {
1610-
private rootRef = createRef<HTMLDivElement>(); // like this
1611-
render() {
1612-
return <div ref={this.rootRef}>{this.props.children}</div>;
1613-
}
1615+
function MyInput(props: ComponentPropsWithRef<"input">) {
1616+
return <input {...props} />;
1617+
}
1618+
1619+
// Usage in parent component
1620+
function Parent() {
1621+
const inputRef = useRef<HTMLInputElement>(null);
1622+
1623+
return <MyInput ref={inputRef} placeholder="Type here..." />;
16141624
}
16151625
```
16161626

1617-
`forwardRef`:
1627+
##### Option 2: Explicit typing
1628+
1629+
If you have custom props and want fine-grained control, you can explicitly type the ref:
1630+
1631+
```tsx
1632+
import { Ref, useRef } from "react";
1633+
1634+
interface MyInputProps {
1635+
placeholder: string;
1636+
ref: Ref<HTMLInputElement>;
1637+
}
1638+
1639+
function MyInput(props: MyInputProps) {
1640+
return <input {...props} />;
1641+
}
1642+
1643+
// Usage in parent component
1644+
function Parent() {
1645+
const inputRef = useRef<HTMLInputElement>(null);
1646+
1647+
return <MyInput ref={inputRef} placeholder="Type here..." />;
1648+
}
1649+
```
1650+
1651+
**Read more**: [Wrapping/Mirroring a HTML Element](/docs/advanced/patterns_by_usecase#wrappingmirroring-a-html-element)
1652+
1653+
#### Legacy Approaches (Pre-React 19)
1654+
1655+
##### forwardRef
1656+
1657+
For React 18 and earlier, use `forwardRef`:
16181658

16191659
```tsx
16201660
import { forwardRef, ReactNode } from "react";
@@ -1648,7 +1688,7 @@ interface Props {
16481688
export const FancyButton = forwardRef(
16491689
(
16501690
props: Props,
1651-
ref: Ref<HTMLButtonElement> // <-- here!
1691+
ref: Ref<HTMLButtonElement> // <-- explicit immutable ref type
16521692
) => (
16531693
<button ref={ref} className="MyClassName" type={props.type}>
16541694
{props.children}
@@ -1659,42 +1699,47 @@ export const FancyButton = forwardRef(
16591699

16601700
</details>
16611701

1662-
If you are grabbing the props of a component that forwards refs, use [`ComponentPropsWithRef`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L770).
1702+
If you need to grab props from a component that forwards refs, use [`ComponentPropsWithRef`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L770).
16631703

1664-
`ref` as a prop:
1704+
##### createRef
16651705

1666-
React 19, you can access ref as prop for function components.
1706+
`createRef` is mostly used for class components. Function components typically rely on `useRef` instead.
16671707

16681708
```tsx
1669-
function MyInput({ placeholder, ref }) {
1670-
return <input placeholder={placeholder} ref={ref} />;
1671-
}
1709+
import { createRef, PureComponent } from "react";
16721710

1673-
// In parent
1674-
<MyInput ref={ref} />;
1711+
class CssThemeProvider extends PureComponent<Props> {
1712+
private rootRef = createRef<HTMLDivElement>();
1713+
1714+
render() {
1715+
return <div ref={this.rootRef}>{this.props.children}</div>;
1716+
}
1717+
}
16751718
```
16761719

1677-
Read more [`ref` as a prop](https://react.dev/blog/2024/12/05/react-19#ref-as-a-prop).
1720+
#### Generic Components with Refs
16781721

1679-
#### Generic forwardRefs
1722+
Generic components typically require manual ref handling since their generic nature prevents automatic type inference. Here are the main approaches:
16801723

1681-
Read more context in https://fettblog.eu/typescript-react-generic-forward-refs/:
1724+
Read more context in [this article](https://fettblog.eu/typescript-react-generic-forward-refs/).
16821725

1683-
##### Option 1 - Wrapper component
1726+
##### Option 1: Wrapper Component
16841727

1685-
```ts
1686-
type ClickableListProps<T> = {
1728+
The most straightforward approach is to manually handle refs through props:
1729+
1730+
```tsx
1731+
interface ClickableListProps<T> {
16871732
items: T[];
16881733
onSelect: (item: T) => void;
16891734
mRef?: React.Ref<HTMLUListElement> | null;
1690-
};
1735+
}
16911736

16921737
export function ClickableList<T>(props: ClickableListProps<T>) {
16931738
return (
16941739
<ul ref={props.mRef}>
16951740
{props.items.map((item, i) => (
16961741
<li key={i}>
1697-
<button onClick={(el) => props.onSelect(item)}>Select</button>
1742+
<button onClick={() => props.onSelect(item)}>Select</button>
16981743
{item}
16991744
</li>
17001745
))}
@@ -1703,17 +1748,19 @@ export function ClickableList<T>(props: ClickableListProps<T>) {
17031748
}
17041749
```
17051750

1706-
##### Option 2 - Redeclare forwardRef
1751+
##### Option 2: Redeclare forwardRef
17071752

1708-
```ts
1709-
// Redeclare forwardRef
1753+
For true `forwardRef` behavior with generics, extend the module declaration:
1754+
1755+
```tsx
1756+
// Redeclare forwardRef to support generics
17101757
declare module "react" {
17111758
function forwardRef<T, P = {}>(
17121759
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
17131760
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
17141761
}
17151762

1716-
// Just write your components like you're used to!
1763+
// Now you can use forwardRef with generics normally
17171764
import { forwardRef, ForwardedRef } from "react";
17181765

17191766
interface ClickableListProps<T> {
@@ -1729,7 +1776,7 @@ function ClickableListInner<T>(
17291776
<ul ref={ref}>
17301777
{props.items.map((item, i) => (
17311778
<li key={i}>
1732-
<button onClick={(el) => props.onSelect(item)}>Select</button>
1779+
<button onClick={() => props.onSelect(item)}>Select</button>
17331780
{item}
17341781
</li>
17351782
))}
@@ -1740,10 +1787,12 @@ function ClickableListInner<T>(
17401787
export const ClickableList = forwardRef(ClickableListInner);
17411788
```
17421789

1743-
##### Option 3 - Call signature
1790+
##### Option 3: Call Signature
17441791

1745-
```ts
1746-
// Add to `index.d.ts`
1792+
If you need both generic support and proper forwardRef behavior with full type inference, you can use the call signature:
1793+
1794+
```tsx
1795+
// Add to your type definitions (e.g. in `index.d.ts` file)
17471796
interface ForwardRefWithGenerics extends React.FC<WithForwardRefProps<Option>> {
17481797
<T extends Option>(props: WithForwardRefProps<T>): ReturnType<
17491798
React.FC<WithForwardRefProps<T>>
@@ -1754,15 +1803,20 @@ export const ClickableListWithForwardRef: ForwardRefWithGenerics =
17541803
forwardRef(ClickableList);
17551804
```
17561805

1757-
Credits: https://stackoverflow.com/a/73795494
1806+
Credits: [https://stackoverflow.com/a/73795494](https://stackoverflow.com/a/73795494)
17581807

1759-
#### More Info
1808+
:::note
1809+
Option 1 is usually sufficient and clearer. Use Option 2 when you specifically need `forwardRef` behavior. Use Option 3 for advanced library scenarios requiring both generics and full forwardRef type inference.
1810+
:::
17601811

1761-
- https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315
1812+
#### Additional Resources
17621813

1763-
You may also wish to do [Conditional Rendering with `forwardRef`](https://github.com/typescript-cheatsheets/react/issues/167).
1814+
- [React refs with TypeScript](https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315)
1815+
- [Conditional rendering with forwardRef](https://github.com/typescript-cheatsheets/react/issues/167)
17641816

1765-
[Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new).
1817+
---
1818+
1819+
[Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new)
17661820

17671821
<!--END-SECTION:forward-create-ref-->
17681822

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/typescript-cheatsheets/react/commit/25d30ac3361c231ef7fe7f7acfe63c29e021460f

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy