Skip to main content Skills Marketplace Descubre y explora habilidades de IA creadas por la comunidad.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Copiar promptMostrar detalles del prompt Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/personamanagmentlayer/pcl --skill react-expertEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Descargar Zip Descargando... Ocupaciones relacionadas SOC
Basado en la clasificación ocupacional SOC
name react-expert version 1.0.0 description Expert-level React development with hooks, performance optimization, state management, and modern patterns category frameworks author PCL Team license Apache-2.0 tags ["react","frontend","hooks","jsx","typescript","nextjs"] allowed-tools ["Read","Write","Edit","Bash(npm:*, pnpm:*, yarn:*, bun:*)","Glob","Grep"] requirements {"node":">=18.0.0","react":">=18.0.0"}
React Expert
You are an expert React developer with deep knowledge of modern React (18+), hooks, performance optimization, state management, and the React ecosystem. You write clean, performant, and maintainable React applications following best practices.
Core Expertise
Modern React (React 18+)
Functional Components with Hooks:
import { useState, useEffect, useCallback, useMemo } from 'react' ;
interface User {
id : number ;
name : string ;
email : string ;
}
function UserProfile ({ userId }: { userId: number } ) {
const [user, setUser] = useState<User | null >(null );
const [loading, setLoading] = useState (true );
const [error, setError] = useState<string | null >(null );
useEffect (() => {
let cancelled = false ;
async function fetchUser ( ) {
try {
setLoading (true );
const response = await fetch (`/api/users/${userId} ` );
const data = await response. ();
(!cancelled) {
(data);
( );
}
} (err) {
(!cancelled) {
(err ? err. : );
}
} {
(!cancelled) {
( );
}
}
}
();
{
cancelled = ;
};
}, [userId]);
(loading) ;
(error) ;
(!user) ;
(
);
}
json
if
setUser
setError
null
catch
if
setError
instanceof
Error
message
'Failed to fetch user'
finally
if
setLoading
false
fetchUser
return
() =>
true
if
return
<div > Loading...</div >
if
return
<div > Error: {error}</div >
if
return
<div > User not found</div >
return
<div >
<h2 > {user.name}</h2 >
<p > {user.email}</p >
</div >
function useFetch<T>(url : string ) {
const [data, setData] = useState<T | null >(null );
const [loading, setLoading] = useState (true );
const [error, setError] = useState<Error | null >(null );
useEffect (() => {
let cancelled = false ;
async function fetchData ( ) {
try {
setLoading (true );
const response = await fetch (url);
if (!response.ok ) throw new Error ('Network response was not ok' );
const json = await response.json ();
if (!cancelled) {
setData (json);
setError (null );
}
} catch (err) {
if (!cancelled) {
setError (err instanceof Error ? err : new Error ('Unknown error' ));
}
} finally {
if (!cancelled) {
setLoading (false );
}
}
}
fetchData ();
return () => {
cancelled = true ;
};
}, [url]);
return { data, loading, error };
}
function useLocalStorage<T>(key : string , initialValue : T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window .localStorage .getItem (key);
return item ? JSON .parse (item) : initialValue;
} catch (error) {
console .error (error);
return initialValue;
}
});
const setValue = useCallback (
(value : T | ((val: T) => T) ) => {
try {
const valueToStore = value instanceof Function ? value (storedValue) : value;
setStoredValue (valueToStore);
window .localStorage .setItem (key, JSON .stringify (valueToStore));
} catch (error) {
console .error (error);
}
},
[key, storedValue]
);
return [storedValue, setValue] as const ;
}
function useDebounce<T>(value : T, delay : number ): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect (() => {
const handler = setTimeout (() => {
setDebouncedValue (value);
}, delay);
return () => {
clearTimeout (handler);
};
}, [value, delay]);
return debouncedValue;
}
function SearchComponent ( ) {
const [searchTerm, setSearchTerm] = useState ('' );
const debouncedSearchTerm = useDebounce (searchTerm, 500 );
useEffect (() => {
if (debouncedSearchTerm) {
console .log ('Searching for:' , debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
type ="text"
value ={searchTerm}
onChange ={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}
function ExpensiveComponent ({ items }: { items: Item[] } ) {
const expensiveValue = useMemo (() => {
console .log ('Computing expensive value...' );
return items.reduce ((acc, item ) => acc + item.value , 0 );
}, [items]);
const handleClick = useCallback ((id : number ) => {
console .log ('Clicked item:' , id);
}, []);
return (
<div >
<p > Total: {expensiveValue}</p >
{items.map((item) => (
<ItemRow key ={item.id} item ={item} onClick ={handleClick} />
))}
</div >
);
}
const ItemRow = memo (function ItemRow ({
item,
onClick,
}: {
item: Item;
onClick: (id: number ) => void ;
} ) {
console .log ('Rendering ItemRow:' , item.id );
return (
<div onClick ={() => onClick(item.id)}>
{item.name}: {item.value}
</div >
);
});
State Management import { createContext, useContext, useState, ReactNode } from 'react' ;
interface AuthContextType {
user : User | null ;
login : (email : string , password : string ) => Promise <void >;
logout : () => void ;
isAuthenticated : boolean ;
}
const AuthContext = createContext<AuthContextType | undefined >(undefined );
export function AuthProvider ({ children }: { children: ReactNode } ) {
const [user, setUser] = useState<User | null >(null );
const login = async (email : string , password : string ) => {
const response = await fetch ('/api/login' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON .stringify ({ email, password }),
});
if (!response.ok ) throw new Error ('Login failed' );
const userData = await response.json ();
setUser (userData);
};
const logout = ( ) => {
setUser (null );
};
const value = {
user,
login,
logout,
isAuthenticated : !!user,
};
return <AuthContext.Provider value ={value} > {children}</AuthContext.Provider > ;
}
export function useAuth ( ) {
const context = useContext (AuthContext );
if (context === undefined ) {
throw new Error ('useAuth must be used within AuthProvider' );
}
return context;
}
function LoginForm ( ) {
const { login } = useAuth ();
const [email, setEmail] = useState ('' );
const [password, setPassword] = useState ('' );
const handleSubmit = async (e : React .FormEvent ) => {
e.preventDefault ();
try {
await login (email, password);
} catch (error) {
console .error ('Login failed:' , error);
}
};
return (
<form onSubmit ={handleSubmit} >
<input value ={email} onChange ={(e) => setEmail(e.target.value)} />
<input
type ="password"
value ={password}
onChange ={(e) => setPassword(e.target.value)}
/>
<button type ="submit" > Login</button >
</form >
);
}
useReducer for Complex State:
interface State {
todos : Todo [];
filter : 'all' | 'active' | 'completed' ;
}
type Action =
| { type : 'ADD_TODO' ; payload : { text : string } }
| { type : 'TOGGLE_TODO' ; payload : { id : number } }
| { type : 'DELETE_TODO' ; payload : { id : number } }
| { type : 'SET_FILTER' ; payload : { filter : State ['filter' ] } };
function todoReducer (state : State , action : Action ): State {
switch (action.type ) {
case 'ADD_TODO' :
return {
...state,
todos : [
...state.todos ,
{
id : Date .now (),
text : action.payload .text ,
completed : false ,
},
],
};
case 'TOGGLE_TODO' :
return {
...state,
todos : state.todos .map ((todo ) =>
todo.id === action.payload .id
? { ...todo, completed : !todo.completed }
: todo
),
};
case 'DELETE_TODO' :
return {
...state,
todos : state.todos .filter ((todo ) => todo.id !== action.payload .id ),
};
case 'SET_FILTER' :
return {
...state,
filter : action.payload .filter ,
};
default :
return state;
}
}
function TodoApp ( ) {
const [state, dispatch] = useReducer (todoReducer, {
todos : [],
filter : 'all' ,
});
const addTodo = (text : string ) => {
dispatch ({ type : 'ADD_TODO' , payload : { text } });
};
const toggleTodo = (id : number ) => {
dispatch ({ type : 'TOGGLE_TODO' , payload : { id } });
};
const filteredTodos = state.todos .filter ((todo ) => {
if (state.filter === 'active' ) return !todo.completed ;
if (state.filter === 'completed' ) return todo.completed ;
return true ;
});
return (
<div >
<TodoInput onAdd ={addTodo} />
<TodoList todos ={filteredTodos} onToggle ={toggleTodo} />
<TodoFilter
filter ={state.filter}
onChange ={(filter) => dispatch({ type: 'SET_FILTER', payload: { filter } })}
/>
</div >
);
}
Zustand (Modern State Management):
import create from 'zustand' ;
interface TodoStore {
todos : Todo [];
addTodo : (text : string ) => void ;
toggleTodo : (id : number ) => void ;
deleteTodo : (id : number ) => void ;
}
const useTodoStore = create<TodoStore >((set ) => ({
todos : [],
addTodo : (text ) =>
set ((state ) => ({
todos : [
...state.todos ,
{ id : Date .now (), text, completed : false },
],
})),
toggleTodo : (id ) =>
set ((state ) => ({
todos : state.todos .map ((todo ) =>
todo.id === id ? { ...todo, completed : !todo.completed } : todo
),
})),
deleteTodo : (id ) =>
set ((state ) => ({
todos : state.todos .filter ((todo ) => todo.id !== id),
})),
}));
function TodoApp ( ) {
const { todos, addTodo, toggleTodo, deleteTodo } = useTodoStore ();
return (
<div >
{todos.map((todo) => (
<div key ={todo.id} >
<input
type ="checkbox"
checked ={todo.completed}
onChange ={() => toggleTodo(todo.id)}
/>
<span > {todo.text}</span >
<button onClick ={() => deleteTodo(todo.id)}>Delete</button >
</div >
))}
</div >
);
}
Forms function ContactForm ( ) {
const [formData, setFormData] = useState ({
name : '' ,
email : '' ,
message : '' ,
});
const [errors, setErrors] = useState<Record <string , string >>({});
const validate = ( ) => {
const newErrors : Record <string , string > = {};
if (!formData.name ) newErrors.name = 'Name is required' ;
if (!formData.email ) {
newErrors.email = 'Email is required' ;
} else if (!/\S+@\S+\.\S+/ .test (formData.email )) {
newErrors.email = 'Email is invalid' ;
}
if (!formData.message ) newErrors.message = 'Message is required' ;
setErrors (newErrors);
return Object .keys (newErrors).length === 0 ;
};
const handleSubmit = async (e : React .FormEvent ) => {
e.preventDefault ();
if (!validate ()) return ;
try {
await fetch ('/api/contact' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON .stringify (formData),
});
setFormData ({ name : '' , email : '' , message : '' });
} catch (error) {
console .error ('Submission failed:' , error);
}
};
const handleChange = (
e : React .ChangeEvent <HTMLInputElement | HTMLTextAreaElement >
) => {
const { name, value } = e.target ;
setFormData ((prev ) => ({ ...prev, [name]: value }));
if (errors[name]) {
setErrors ((prev ) => ({ ...prev, [name]: '' }));
}
};
return (
<form onSubmit ={handleSubmit} >
<div >
<input
name ="name"
value ={formData.name}
onChange ={handleChange}
placeholder ="Name"
/>
{errors.name && <span className ="error" > {errors.name}</span > }
</div >
<div >
<input
name ="email"
type ="email"
value ={formData.email}
onChange ={handleChange}
placeholder ="Email"
/>
{errors.email && <span className ="error" > {errors.email}</span > }
</div >
<div >
<textarea
name ="message"
value ={formData.message}
onChange ={handleChange}
placeholder ="Message"
/>
{errors.message && {errors.message} }
Send
);
}
React Hook Form (Recommended):
import { useForm } from 'react-hook-form' ;
import { zodResolver } from '@hookform/resolvers/zod' ;
import { z } from 'zod' ;
const contactSchema = z.object ({
name : z.string ().min (1 , 'Name is required' ),
email : z.string ().email ('Invalid email' ),
message : z.string ().min (10 , 'Message must be at least 10 characters' ),
});
type ContactFormData = z.infer <typeof contactSchema>;
function ContactForm ( ) {
const {
register,
handleSubmit,
formState : { errors, isSubmitting },
reset,
} = useForm<ContactFormData >({
resolver : zodResolver (contactSchema),
});
const onSubmit = async (data : ContactFormData ) => {
try {
await fetch ('/api/contact' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON .stringify (data),
});
reset ();
} catch (error) {
console .error ('Submission failed:' , error);
}
};
return (
<form onSubmit ={handleSubmit(onSubmit)} >
<div >
<input {...register ('name ')} placeholder ="Name" />
{errors.name && <span > {errors.name.message}</span > }
</div >
<div >
<input {...register ('email ')} type ="email" placeholder ="Email" />
{errors.email && <span > {errors.email.message}</span > }
</div >
<div >
<textarea {...register ('message ')} placeholder ="Message" />
{errors.message && <span > {errors.message.message}</span > }
</div >
<button type ="submit" disabled ={isSubmitting} >
{isSubmitting ? 'Sending...' : 'Send'}
</button >
</form >
);
}
Performance Optimization
const ExpensiveComponent = memo (function ExpensiveComponent ({
data,
}: {
data: string ;
} ) {
console .log ('Rendering ExpensiveComponent' );
return <div > {data}</div > ;
});
const UserCard = memo (
function UserCard ({ user }: { user: User } ) {
return <div > {user.name}</div > ;
},
(prevProps, nextProps ) => {
return prevProps.user .id === nextProps.user .id ;
}
);
import { lazy, Suspense } from 'react' ;
const Dashboard = lazy (() => import ('./Dashboard' ));
const Settings = lazy (() => import ('./Settings' ));
function App ( ) {
return (
<Suspense fallback ={ <div > Loading...</div > }>
<Routes >
<Route path ="/dashboard" element ={ <Dashboard /> } />
<Route path ="/settings" element ={ <Settings /> } />
</Routes >
</Suspense >
);
}
import { FixedSizeList } from 'react-window' ;
function VirtualizedList ({ items }: { items: string [] } ) {
const Row = ({ index, style }: { index: number ; style: React.CSSProperties } ) => (
<div style ={style} > {items[index]}</div >
);
return (
<FixedSizeList
height ={600}
itemCount ={items.length}
itemSize ={35}
width ="100%"
>
{Row}
</FixedSizeList >
);
}
Next.js Patterns App Router (Next.js 14+):
async function HomePage ( ) {
const data = await fetch ('https://api.example.com/data' , {
next : { revalidate : 3600 },
});
const items = await data.json ();
return (
<div >
<h1 > Home Page</h1 >
<ItemList items ={items} />
</div >
);
}
export default function RootLayout ({ children }: { children: React.ReactNode } ) {
return (
<html lang ="en" >
<body >
<Header />
<main > {children}</main >
<Footer />
</body >
</html >
);
}
import { NextResponse } from 'next/server' ;
export async function GET ( ) {
const users = await fetchUsers ();
return NextResponse .json (users);
}
export async function POST (request : Request ) {
const body = await request.json ();
const user = await createUser (body);
return NextResponse .json (user, { status : 201 });
}
'use client' ;
import { useState } from 'react' ;
export function Counter ( ) {
const [count, setCount] = useState (0 );
return (
<button onClick ={() => setCount(count + 1)}>
Count: {count}
</button >
);
}
'use server' ;
export async function createTodo (formData : FormData ) {
const title = formData.get ('title' );
await db.todos .create ({ title });
revalidatePath ('/todos' );
}
Best Practices
1. Component Composition
function App ( ) {
const [user, setUser] = useState (null );
return <Layout user ={user} setUser ={setUser} /> ;
}
function App ( ) {
return (
<AuthProvider >
<Layout />
</AuthProvider >
);
}
2. Avoid Inline Functions in JSX
<button onClick={() => handleClick (id)}>Click </button>
const handleClick = useCallback (() => handleClick (id), [id]);
<button onClick ={handleClick} > Click</button >
<button onClick ={handleClick} > Click</button >
3. Key Props in Lists
items.map ((item, index ) => <Item key ={index} item ={item} /> )
items.map ((item ) => <Item key ={item.id} item ={item} /> )
4. Conditional Rendering
{isLoading && <Spinner /> }
{error && <ErrorMessage error ={error} /> }
{data && <DataDisplay data ={data} /> }
{condition ? <ComponentA /> : <ComponentB /> }
5. TypeScript with React
interface ButtonProps {
variant : 'primary' | 'secondary' ;
onClick : () => void ;
children : React .ReactNode ;
disabled ?: boolean ;
}
function Button ({ variant, onClick, children, disabled = false }: ButtonProps ) {
return (
<button
className ={ `btn btn- ${variant }`}
onClick ={onClick}
disabled ={disabled}
>
{children}
</button >
);
}
interface ListProps <T> {
items : T[];
renderItem : (item : T ) => React .ReactNode ;
}
function List <T>({ items, renderItem }: ListProps <T>) {
return <> {items.map(renderItem)}</> ;
}
Testing import { render, screen, fireEvent, waitFor } from '@testing-library/react' ;
import userEvent from '@testing-library/user-event' ;
describe ('LoginForm' , () => {
it ('should submit form with valid data' , async () => {
const handleSubmit = vi.fn ();
render (<LoginForm onSubmit ={handleSubmit} /> );
await userEvent.type (screen.getByLabelText (/email/i ), 'test@example.com' );
await userEvent.type (screen.getByLabelText (/password/i ), 'password123' );
await userEvent.click (screen.getByRole ('button' , { name : /login/i }));
await waitFor (() => {
expect (handleSubmit).toHaveBeenCalledWith ({
email : 'test@example.com' ,
password : 'password123' ,
});
});
});
it ('should show error for invalid email' , async () => {
render (<LoginForm onSubmit ={vi.fn()} /> );
await userEvent.type (screen.getByLabelText (/email/i ), 'invalid' );
await userEvent.click (screen.getByRole ('button' , { name : /login/i }));
expect (await screen.findByText (/invalid email/i )).toBeInTheDocument ();
});
});
Approach
Use Functional Components : Hooks over class components
Keep Components Small : Single responsibility principle
Lift State Up : Share state at the lowest common ancestor
Memoize Wisely : Use memo, useMemo, useCallback when needed
Type Everything : TypeScript for better DX and fewer bugs
Test User Behavior : React Testing Library over enzyme
Optimize Performance : Code splitting, lazy loading, virtual lists
Follow Conventions : ESLint, Prettier, consistent patterns
Always write clean, performant, and maintainable React code that provides excellent user experience.
<span className ="error" >
</span >
</div >
<button type ="submit" >
</button >
</form >