useState with TypeScript
import React, { useState } from 'react';
interface User {
name: string;
age: number;
}
function UserProfile() {
const [user, setUser] = useState<User | null>(null);
const updateUser = () => {
setUser({ name: 'John Doe', age: 30 });
};
return (
<div>
{user ? (
<>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
</>
) : (
<button onClick={updateUser}>Set User</button>
)}
</div>
);
}
useEffect Cleanup
import React, { useState, useEffect } from 'react';
function SubscriptionComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
const subscription = subscribeToData(newData => {
if (isMounted) {
setData(newData);
}
});
return () => {
isMounted = false;
subscription.unsubscribe();
};
}, []);
return <div>{data ? `Data: ${data}` : 'Loading...'}</div>;
}
useContext with TypeScript
import React, { createContext, useContext, useState } from 'react';
interface ThemeContextType {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export const ThemeProvider: React.FC = ({ children }) => {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
// Usage
const ThemedButton: React.FC = () => {
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme} style={{ background: theme === 'light' ? '#fff' : '#333' }}>
Toggle Theme
</button>
);
};
React.memo with Custom Comparison
import React, { memo } from 'react';
const ExpensiveComponent = memo(({ data }) => {
// Render using data
}, (prevProps, nextProps) => {
// Custom comparison function
return prevProps.data.id === nextProps.data.id;
});
// Usage
function ParentComponent() {
const data = { id: 1, value: Math.random() };
return <ExpensiveComponent data={data} />;
}
useMemo for Expensive Calculations
import React, { useState, useMemo } from 'react';
function Fibonacci({ n }) {
const result = useMemo(() => {
const fib = (num) => {
if (num <= 1) return num;
return fib(num - 1) + fib(num - 2);
};
return fib(n);
}, [n]);
return <div>Fibonacci of {n} is {result}</div>;
}
Render Props Pattern
import React, { useState } from 'react';
const Toggle = ({ children }) => {
const [on, setOn] = useState(false);
const toggle = () => setOn(!on);
return children({ on, toggle });
};
// Usage
function App() {
return (
<Toggle>
{({ on, toggle }) => (
<div>
{on ? 'ON' : 'OFF'}
<button onClick={toggle}>Toggle</button>
</div>
)}
</Toggle>
);
}
Compound Components Pattern
import React, { createContext, useContext, useState } from 'react';
const ToggleContext = createContext();
function Toggle({ children }) {
const [on, setOn] = useState(false);
const toggle = () => setOn(!on);
return (
<ToggleContext.Provider value={{ on, toggle }}>
{children}
</ToggleContext.Provider>
);
}
Toggle.On = ({ children }) => {
const { on } = useContext(ToggleContext);
return on ? children : null;
};
Toggle.Off = ({ children }) => {
const { on } = useContext(ToggleContext);
return on ? null : children;
};
Toggle.Button = () => {
const { on, toggle } = useContext(ToggleContext);
return <button onClick={toggle}>{on ? 'ON' : 'OFF'}</button>;
};
// Usage
function App() {
return (
<Toggle>
<Toggle.On>The button is on</Toggle.On>
<Toggle.Off>The button is off</Toggle.Off>
<Toggle.Button />
</Toggle>
);
}
useReducer with TypeScript
import React, { useReducer } from 'react';
type State = {
count: number;
};
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset' };
const initialState: State = { count: 0 };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return initialState;
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
forwardRef and useImperativeHandle
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
getValue: () => {
return inputRef.current.value;
}
}));
return <input ref={inputRef} {...props} />;
});
// Usage
function Form() {
const fancyInputRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
console.log(fancyInputRef.current.getValue());
};
return (
<form onSubmit={handleSubmit}>
<FancyInput ref={fancyInputRef} />
<button type="submit">Submit</button>
</form>
);
}
Testing Hooks with React Testing Library
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
function Counter() {
const [count, setCount] = React.useState(0);
return (
<>
<span data-testid="count">{count}</span>
<button onClick={() => setCount(count + 1)}>Increment</button>
</>
);
}
test('Counter increments when button is clicked', () => {
const { getByTestId, getByText } = render(<Counter />);
const countElement = getByTestId('count');
const incrementButton = getByText('Increment');
expect(countElement).toHaveTextContent('0');
fireEvent.click(incrementButton);
expect(countElement).toHaveTextContent('1');
});
Mocking Context in Tests
import React, { useContext } from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme === 'light' ? '#fff' : '#000' }}>Theme: {theme}</button>;
}
test('ThemedButton uses the correct theme', () => {
const { getByText } = render(
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
const button = getByText('Theme: dark');
expect(button).toHaveStyle('background: #000');
});
Styled-components with TypeScript
import styled from 'styled-components';
interface ButtonProps {
primary?: boolean;
backgroundColor?: string;
}
const Button = styled.button<ButtonProps>`
background-color: ${(props) =>
props.backgroundColor || (props.primary ? '#007bff' : '#6c757d')};
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
opacity: 0.8;
}
`;
// Usage
function App() {
return (
<>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
<Button backgroundColor="#28a745">Custom Button</Button>
</>
);
}
Custom Hook: useLocalStorage
import { useState, useEffect } from 'react';
function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(storedValue));
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
// Usage
function App() {
const [name, setName] = useLocalStorage<string>('name', 'John');
return (
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
);
}
React.lazy and Suspense
import React, { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<h1>My App</h1>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
Higher-Order Component (HOC) Pattern
import React from 'react';
function withLogging(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} mounted`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
// Usage
const MyComponent = ({ name }) => <div>Hello, {name}!</div>;
const EnhancedComponent = withLogging(MyComponent);
function App() {
return <EnhancedComponent name="John" />;
}