React has become one of the most popular JavaScript libraries for building user interfaces, particularly single-page applications. As a senior developer, mastering advanced React techniques is crucial for creating efficient, scalable, and maintainable applications. This article delves into 20 advanced React techniques that every senior developer should know, complete with TypeScript examples where applicable.
1. Higher-Order Components (HOCs)
Higher-Order Components are a pattern in React for reusing component logic. An HOC is a function that takes a component and returns a new component.
import React from 'react';
const withLogger = (WrappedComponent: React.ComponentType) => {
return class extends React.Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} mounted`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
};
const MyComponent: React.FC = () => <div>Hello World</div>;
const MyComponentWithLogger = withLogger(MyComponent);
2. Render Props
Render props are a technique for sharing code between React components using a prop whose value is a function.
import React from 'react';
interface DataFetcherProps {
render: (data: any) => JSX.Element;
}
const DataFetcher: React.FC<DataFetcherProps> = ({ render }) => {
const data = { name: 'John Doe' };
return render(data);
};
const MyComponent: React.FC = () => (
<DataFetcher render={(data) => <div>{data.name}</div>} />
);
3. Context API
The Context API is a React structure that enables you to exchange unique details and assists in solving prop-drilling from the top parent component to children.
import React, { createContext, useContext } from 'react';
const MyContext = createContext<string | null>(null);
const MyProvider: React.FC = ({ children }) => {
const value = 'Hello from Context';
return <MyContext.Provider value={value}>{children}</MyContext.Provider>;
};
const MyComponent: React.FC = () => {
const value = useContext(MyContext);
return <div>{value}</div>;
};
4. Custom Hooks
Custom hooks allow you to encapsulate and reuse stateful logic.
import { useState, useEffect } from 'react';
const useFetch = (url: string) => {
const [data, setData] = useState<any>(null);
useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((data) => setData(data));
}, [url]);
return data;
};
const MyComponent: React.FC = () => {
const data = useFetch('https://api.example.com/data');
return <div>{data ? data.name : 'Loading...'}</div>;
};
5. Error Boundaries
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI.
import React from 'react';
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error: any) {
return { hasError: true };
}
componentDidCatch(error: any, errorInfo: any) {
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
const MyComponent: React.FC = () => {
throw new Error('Test error');
return <div>Hello World</div>;
};
const App: React.FC = () => (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
6. Code Splitting
Code splitting is a feature supported by bundlers like Webpack, Rollup, and Browserify (via factor-bundle) which can create multiple bundles that can be dynamically loaded at runtime.
import React, { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
const MyComponent: React.FC = () => (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
7. Memoization
Memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
import React, { useMemo } from 'react';
const MyComponent: React.FC<{ items: number[] }> = ({ items }) => {
const sortedItems = useMemo(() => items.sort(), [items]);
return <div>{sortedItems.join(', ')}</div>;
};
8. Portals
Portals provide a way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
import React from 'react';
import ReactDOM from 'react-dom';
const MyPortal: React.FC = () => {
return ReactDOM.createPortal(
<div>This is rendered in a portal</div>,
document.getElementById('portal-root')!
);
};
9. Fragments
Fragments let you group a list of children without adding extra nodes to the DOM.
import React, { Fragment } from 'react';
const MyComponent: React.FC = () => (
<Fragment>
<div>Item 1</div>
<div>Item 2</div>
</Fragment>
);
10. Refs and the DOM
Refs provide a way to access DOM nodes or React elements created in the render method.
import React, { useRef, useEffect } from 'react';
const MyComponent: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
};
11. Forwarding Refs
Ref forwarding is a technique for automatically passing a ref through a component to one of its children.
import React, { forwardRef, useRef } from 'react';
const MyInput = forwardRef<HTMLInputElement, {}>((props, ref) => (
<input ref={ref} type="text" />
));
const MyComponent: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null);
return <MyInput ref={inputRef} />;
};
12. Controlled and Uncontrolled Components
Controlled components have their state and behavior controlled by the parent component, while uncontrolled components manage their own state.
import React, { useState } from 'react';
const ControlledComponent: React.FC = () => {
const [value, setValue] = useState('');
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
};
const UncontrolledComponent: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null);
return <input ref={inputRef} type="text" />;
};
13. Performance Optimization
Performance optimization techniques include using React.memo
, useMemo
, and useCallback
to prevent unnecessary re-renders.
import React, { useCallback, memo } from 'react';
const MyComponent: React.FC<{ onClick: () => void }> = memo(({ onClick }) => {
console.log('Rendering MyComponent');
return <button onClick={onClick}>Click me</button>;
});
const ParentComponent: React.FC = () => {
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
return <MyComponent onClick={handleClick} />;
};
14. Server-Side Rendering (SSR)
Server-Side Rendering is a technique used to render a normally client-side only single-page application (SPA) on the server and then send a fully rendered page to the client.
import React from 'react';
import { renderToString } from 'react-dom/server';
const MyComponent: React.FC = () => <div>Hello World</div>;
const html = renderToString(<MyComponent />);
15. Static Site Generation (SSG)
Static Site Generation is a technique used to pre-render pages at build time.
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
const MyComponent: React.FC = () => <div>Hello World</div>;
const html = renderToStaticMarkup(<MyComponent />);
16. Incremental Static Regeneration (ISR)
Incremental Static Regeneration allows you to update static content after you’ve built your site.
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
const MyComponent: React.FC = () => <div>Hello World</div>;
const html = renderToStaticMarkup(<MyComponent />);
17. Concurrent Mode
Concurrent Mode is a set of new features that help React apps stay responsive and gracefully adjust to the user's device capabilities and network speed.
import React, { useState, useTransition } from 'react';
const MyComponent: React.FC = () => {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
const handleClick = () => {
startTransition(() => {
setCount(count + 1);
});
};
return (
<div>
<button onClick={handleClick}>Increment</button>
{isPending ? 'Loading...' : `Count: ${count}`}
</div>
);
};
18. Suspense for Data Fetching
Suspense for Data Fetching allows you to wait for some code to load and declaratively specify a loading state (like a spinner) for it.
import React, { Suspense } from 'react';
const MyComponent: React.FC = () => {
const data = fetchData();
return <div>{data.name}</div>;
};
const App: React.FC = () => (
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
);
19. React Query
React Query is a powerful data-fetching library for React that makes it easy to fetch, cache, synchronize, and update server state in your React applications.
import React from 'react';
import { useQuery } from 'react-query';
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
};
const MyComponent: React.FC = () => {
const { data, error, isLoading } = useQuery('data', fetchData);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{data.name}</div>;
};
20. React Server Components
React Server Components allow you to build apps that span the client and server, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering.
import React from 'react';
const MyServerComponent: React.FC = () => {
const data = fetchDataFromServer();
return <div>{data.name}</div>;
};
const MyClientComponent: React.FC = () => {
return <MyServerComponent />;
};
Conclusion
Mastering these 20 advanced React techniques will significantly enhance your ability to build robust, efficient, and maintainable React applications. Whether you're optimizing performance, managing state, or leveraging server-side rendering, these techniques provide a comprehensive toolkit for senior React developers. By incorporating these practices into your development workflow, you'll be well-equipped to tackle complex challenges and deliver high-quality applications.
Top comments (0)