nik72619c
@niksharma1997
Async transitions
Server Components
use()
New form hooks
Actions
API updates
React Compiler
Server Components
use()
New form APIs
Actions
API updates
I love to talk about performance, react and design systems
nik72619c
@niksharma1997
Babel plugin
Compiler core
Eslint plugin
Babel plugin
Compiler core
Eslint plugin
useMemo()
useCallback()
npx react-compiler-healthcheck@experimental
npm install eslint-plugin-react-compiler@experimental
// Add the config
module.exports = {
plugins: [
'eslint-plugin-react-compiler',
],
rules: {
'react-compiler/react-compiler': "error",
},
}
/GET
/GET
/GET
/GET
/GET
JS bundle ⚠️
JS bundle ⚠️
?
/GET
/GET
/GET
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
Can access db or other server resources
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
Cannot use interactive behaviour
'use client';
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
Specify what parts are interactive
'use client';
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
JS bundle ⚠️
JS bundle
import React, { useState } from 'react';
const SimpleForm = () => {
const [inputValue, setInputValue] = useState('');
const handleChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
alert(`Submitted value: ${inputValue}`);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={handleChange}
/>
<button type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Current state
import React, { useState } from 'react';
const SimpleForm = () => {
const [inputValue, setInputValue] = useState('');
const [isPending, setPending] = useState(false);
const handleChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = (e) => {
setPending(true);
e.preventDefault();
alert(`Submitted value: ${inputValue}`);
setPending(false);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={handleChange}
/>
<button disabled={isPending} type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Current state
More complexity can add in over time
import React, { useState } from 'react';
const SimpleForm = () => {
const [isPending, setPending] = useState(false);
const handleSubmit = (formData) => {
setPending(true);
let inputValue = formData.get('username');
alert(`Submitted value: ${inputValue}`);
setPending(false);
};
return (
<form action={handleSubmit}>
<input
type="text"
name="username"
/>
<button disabled={isPending} type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Actions!!
Actions reduce the need of boilerplate code for managing input states
import React, { useState } from 'react';
const SimpleForm = () => {
const [isPending, startTransition] = useTransition();
const handleSubmit = (formData) => {
startTransition(async () => {
let inputValue = formData.get('username');
alert(`Submitted value: ${inputValue}`);
});
};
return (
<form action={handleSubmit}>
<input
type="text"
name="username"
/>
<button disabled={isPending} type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
Async functions in useTransition supported now
import React, { useActionState } from 'react';
const SimpleForm = () => {
const [state, action, isPending] = useActionState(handleSubmit, {
user: '',
error: null
});
const handleSubmit = async (formData) => {
try {
let inputValue = formData.get('username');
let response = await loginUser(inputValue);
alert(`Submitted value: ${inputValue}`);
return { user: inputValue, error: null };
} catch (error) {
return { user: null, error: error };
}
};
return (
<form action={action}>
<input
type="text"
name="username"
/>
<button disabled={isPending} type="submit">Submit</button>
</form>
);
};
export default SimpleForm;
useActionState
Helps in managing the form states as well
function Submit() {
const { pending, formData, method, action } = useFormStatus();
return <button disabled={pending}>Submit</button>
}
useFormStatus
Helps in knowing the status of the last submitted form
function Submit() {
const { pending, formData, method, action } = useFormStatus();
return <button disabled={pending}>Submit</button>
}
function Todos({todosPromise}) {
// `use` will suspend until the promise resolves.
const todos = use(todosPromise);
return todos.map(todo => <p key={todo.id}>{todo.title}</p>);
}
function Page({todosPromise}) {
// When `use` suspends in Todos,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Todos todosPromise={todosPromise} />
</Suspense>
)
}
promises
function Todos({todosPromise}) {
// `use` will suspend until the promise resolves.
const todos = use(todosPromise);
return todos.map(todo => <p key={todo.id}>{todo.title}</p>);
}
function Page({todosPromise}) {
// When `use` suspends in Todos,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Todos todosPromise={todosPromise} />
</Suspense>
)
}
promises
function Todos({todosPromise}) {
// `use` will suspend until the promise resolves.
const todos = use(todosPromise);
return todos.map(todo => <p key={todo.id}>{todo.title}</p>);
}
function Page({todosPromise}) {
// When `use` suspends in Todos,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Todos todosPromise={todosPromise} />
</Suspense>
)
}
promises
promises
function Todo({children}) {
if (children == null) {
return null;
}
// This would not work with useContext
// because of the early return.
const theme = use(ThemeContext);
return (
<h1 style={{color: theme.color}}>
{children}
</h1>
);
}
Context
function Todos({todosPromise}) {
// `use` will suspend until the promise resolves.
const todos = use(todosPromise);
return todos.map(todo => <p key={todo.id}>{todo.title}</p>);
}
function Page({todosPromise}) {
// When `use` suspends in Todos,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Todos todosPromise={todosPromise} />
</Suspense>
)
}
function CustomInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<CustomInput ref={ref} />
<input
ref={(ref) => {
// ref created
// NEW: return a cleanup function to reset
// the ref when element is removed from DOM.
return () => {
// ref cleanup
ref.removeEventHandler('change', handleOnChange);
};
}}
/>
// Before
function App({children}) {
return (
<ThemeContext.Provider value="dark">
{children}
</ThemeContext.Provider>
);
}
// After
function App({children}) {
return (
<ThemeContext value="dark">
{children}
</ThemeContext>
);
}
BEFORE
AFTER
function MyComponent (props) {
return (
<div>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<h1>React 19 is super awesome!!</h1>
</div>
);
}
<html>
<head>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
</head>
<body>
<div>
<h1>React 19 is super awesome!!</h1>
</div>
</body>
</html>
function MyComponent (props) {
return (
<div>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<link
rel='stylesheet'
href='/styles/modal.css'
precedence='default'
/>
<h1>React 19 is super awesome!!</h1>
</div>
);
}
<html>
<head>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<link
rel='stylesheet'
href='/styles/modal.css'
/>
</head>
<body>
<div>
<h1>React 19 is super awesome!!</h1>
</div>
</body>
</html>
function MyComponent (props) {
return (
<div>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<Suspense fallback='loading contents'>
<link
rel='stylesheet'
href='/styles/modal.css'
precedence='default'
/>
</Suspense>
<h1>React 19 is super awesome!!</h1>
</div>
);
}
function MyComponent (props) {
return (
<div>
<title>React is turning 19!</title>
<meta name='Author' content='Nikhil Sharma' />
<meta
property='og:image'
content='https://dummy-img.com'
/>
<Suspense fallback='loading contents'>
<link
rel='stylesheet'
href='/styles/modal.css'
precedence='default'
/>
</Suspense>
<h1>React 19 is super awesome!!</h1>
</div>
);
}
React 19 is super awesome!!
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
preinit('https://.../path/to/some/script.js', {as: 'script' })
preload('https://.../path/to/font.woff', { as: 'font' })
preload('https://.../path/to/stylesheet.css', { as: 'style' })
prefetchDNS('https://...')
preconnect('https://...')
}
<html>
<head>
<!-- links/scripts are prioritized by their utility to early loading, not call order -->
<link rel="prefetch-dns" href="https://...">
<link rel="preconnect" href="https://...">
<link rel="preload" as="font" href="https://.../path/to/font.woff">
<link rel="preload" as="style" href="https://.../path/to/stylesheet.css">
<script async="" src="https://.../path/to/some/script.js"></script>
</head>
<body>
...
</body>
</html>
npm install --save-exact react@rc react-dom@rc
{
"dependencies": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
},
"overrides": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
}
}
// Before
import {render} from 'react-dom';
render(<App />, document.getElementById('root'));
// After
import {createRoot} from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
npx codemod@latest react/19/migration-recipe
nik72619c
@niksharma1997