React Modernization by @wshobson · Skillet
React Modernization Upgrade React applications to latest versions, migrate from class components to hooks, and adopt concurrent features. Use when modernizing React codebases, migrating to React Hooks, or upgrading to latest React versions.
Run commands Use the internet Install
It’s free, and every skill you add syncs into every AI tool on your computer, instantly.
No event pooling
Effect cleanup timing
JSX transform (no React import needed)
Automatic batching
Concurrent rendering
Strict Mode changes (double invocation)
New root API
Suspense on server
Class to Hooks Migration
State Management
class Counter extends React.Component {
constructor (props ) {
super (props);
this .state = {
count : 0 ,
name : "" ,
};
}
increment = () => {
this .setState ({ count : this .state .count + 1 });
};
render ( ) {
return (
<div >
<p > Count: {this.state.count}</p >
<button onClick ={this.increment} > Increment</button >
</div >
);
}
}
function Counter ( ) {
const [count, setCount] = useState (0 );
const [name, setName] = useState ("" );
const increment = ( ) => {
setCount (count + 1 );
};
return (
<div >
<p > Count: {count}</p >
<button onClick ={increment} > Increment</button >
</div >
);
}
Lifecycle Methods to Hooks
class DataFetcher extends React.Component {
state = { data : null , loading : true };
componentDidMount ( ) {
this .fetchData ();
}
componentDidUpdate (prevProps ) {
if (prevProps.id !== this .props .id ) {
this .fetchData ();
}
}
componentWillUnmount ( ) {
this .cancelRequest ();
}
fetchData = async () => {
const data = await fetch (`/api/${this .props.id} ` );
this .setState ({ data, loading : false });
};
cancelRequest = () => {
};
render ( ) {
if (this .state .loading ) return <div > Loading...</div > ;
return <div > {this.state.data}</div > ;
}
}
function DataFetcher ({ id } ) {
const [data, setData] = useState (null );
const [loading, setLoading] = useState (true );
useEffect (() => {
let cancelled = false ;
const fetchData = async ( ) => {
try {
const response = await fetch (`/api/${id} ` );
const result = await response.json ();
if (!cancelled) {
setData (result);
setLoading (false );
}
} catch (error) {
if (!cancelled) {
console .error (error);
}
}
};
fetchData ();
return () => {
cancelled = true ;
};
}, [id]);
if (loading) return <div > Loading...</div > ;
return <div > {data}</div > ;
}
Context and HOCs to Hooks
const ThemeContext = React .createContext ();
class ThemedButton extends React.Component {
static contextType = ThemeContext ;
render ( ) {
return (
<button style ={{ background: this.context.theme }}>
{this.props.children}
</button >
);
}
}
function ThemedButton ({ children } ) {
const { theme } = useContext (ThemeContext );
return <button style ={{ background: theme }}> {children}</button > ;
}
function withUser (Component ) {
return class extends React .Component {
state = { user : null };
componentDidMount ( ) {
fetchUser ().then ((user ) => this .setState ({ user }));
}
render ( ) {
return <Component {...this.props } user ={this.state.user} /> ;
}
};
}
function useUser ( ) {
const [user, setUser] = useState (null );
useEffect (() => {
fetchUser ().then (setUser);
}, []);
return user;
}
function UserProfile ( ) {
const user = useUser ();
if (!user) return <div > Loading...</div > ;
return <div > {user.name}</div > ;
}
React 18 Concurrent Features
New Root API
import ReactDOM from "react-dom" ;
ReactDOM .render (<App /> , document .getElementById ("root" ));
import { createRoot } from "react-dom/client" ;
const root = createRoot (document .getElementById ("root" ));
root.render (<App /> );
Automatic Batching
function handleClick ( ) {
setCount ((c ) => c + 1 );
setFlag ((f ) => !f);
}
setTimeout (() => {
setCount ((c ) => c + 1 );
setFlag ((f ) => !f);
}, 1000 );
import { flushSync } from "react-dom" ;
flushSync (() => {
setCount ((c ) => c + 1 );
});
setFlag ((f ) => !f);
Transitions import { useState, useTransition } from "react" ;
function SearchResults ( ) {
const [query, setQuery] = useState ("" );
const [results, setResults] = useState ([]);
const [isPending, startTransition] = useTransition ();
const handleChange = (e ) => {
setQuery (e.target .value );
startTransition (() => {
setResults (searchResults (e.target .value ));
});
};
return (
<>
<input value ={query} onChange ={handleChange} />
{isPending && <Spinner /> }
<Results data ={results} />
</>
);
}
Suspense for Data Fetching import { Suspense } from "react" ;
const resource = fetchProfileData ();
function ProfilePage ( ) {
return (
<Suspense fallback ={ <Loading /> }>
<ProfileDetails />
<Suspense fallback ={ <Loading /> }>
<ProfileTimeline />
</Suspense >
</Suspense >
);
}
function ProfileDetails ( ) {
const user = resource.user .read ();
return <h1 > {user.name}</h1 > ;
}
function ProfileTimeline ( ) {
const posts = resource.posts .read ();
return <Timeline posts ={posts} /> ;
}
Additional patterns and templates More detailed templates and worked examples live in references/details.md. Read that file for the full pattern library.
~1.6K tokens