React
1 Introduction to React
1-1 What is React?
1-2 History and Evolution of React
1-3 Key Features of React
1-4 Setting Up the Development Environment
2 JSX and Components
2-1 Introduction to JSX
2-2 Writing JSX Syntax
2-3 Creating Components
2-4 Functional vs Class Components
2-5 Props and State
3 React State Management
3-1 Understanding State
3-2 Managing State in Functional Components
3-3 Managing State in Class Components
3-4 Lifting State Up
3-5 Context API
4 React Hooks
4-1 Introduction to Hooks
4-2 useState Hook
4-3 useEffect Hook
4-4 useContext Hook
4-5 Custom Hooks
5 React Router
5-1 Introduction to React Router
5-2 Setting Up React Router
5-3 Route, Link, and NavLink
5-4 Nested Routes
5-5 Programmatic Navigation
6 Handling Events in React
6-1 Introduction to Events
6-2 Handling Events in Functional Components
6-3 Handling Events in Class Components
6-4 Synthetic Events
6-5 Event Bubbling and Capturing
7 Forms and Controlled Components
7-1 Introduction to Forms in React
7-2 Controlled Components
7-3 Handling Form Submission
7-4 Form Validation
7-5 Uncontrolled Components
8 React Lifecycle Methods
8-1 Introduction to Lifecycle Methods
8-2 Component Mounting Phase
8-3 Component Updating Phase
8-4 Component Unmounting Phase
8-5 Error Handling
9 React and APIs
9-1 Introduction to APIs
9-2 Fetching Data with useEffect
9-3 Handling API Errors
9-4 Caching API Responses
9-5 Real-time Data with WebSockets
10 React Performance Optimization
10-1 Introduction to Performance Optimization
10-2 React memo and PureComponent
10-3 useCallback and useMemo Hooks
10-4 Lazy Loading Components
10-5 Code Splitting
11 React Testing
11-1 Introduction to Testing in React
11-2 Writing Unit Tests with Jest
11-3 Testing Components with React Testing Library
11-4 Mocking Dependencies
11-5 End-to-End Testing with Cypress
12 Advanced React Patterns
12-1 Higher-Order Components (HOC)
12-2 Render Props
12-3 Compound Components
12-4 Context and Provider Pattern
12-5 Custom Hooks for Reusability
13 React and TypeScript
13-1 Introduction to TypeScript
13-2 Setting Up TypeScript with React
13-3 TypeScript Basics for React
13-4 TypeScript with Hooks
13-5 TypeScript with React Router
14 React and Redux
14-1 Introduction to Redux
14-2 Setting Up Redux with React
14-3 Actions, Reducers, and Store
14-4 Connecting React Components to Redux
14-5 Middleware and Async Actions
15 React and GraphQL
15-1 Introduction to GraphQL
15-2 Setting Up GraphQL with React
15-3 Querying Data with Apollo Client
15-4 Mutations and Subscriptions
15-5 Caching and Optimistic UI
16 React Native
16-1 Introduction to React Native
16-2 Setting Up React Native Development Environment
16-3 Building a Simple App
16-4 Navigation in React Native
16-5 Styling and Animations
17 Deployment and Best Practices
17-1 Introduction to Deployment
17-2 Deploying React Apps to GitHub Pages
17-3 Deploying React Apps to Netlify
17-4 Deploying React Apps to AWS
17-5 Best Practices for React Development
Custom Hooks for Reusability

Custom Hooks for Reusability

Key Concepts

What is a Custom Hook?

A Custom Hook is a JavaScript function that starts with the word "use" and can call other Hooks. Custom Hooks allow you to extract component logic into reusable functions, making your code more modular and easier to maintain.

Creating a Custom Hook

To create a Custom Hook, define a function that starts with "use" and encapsulate the logic you want to reuse. This function can then be called from any component that needs the logic.

Example:

        function useToggle(initialValue = false) {
            const [value, setValue] = useState(initialValue);
            const toggle = () => setValue(!value);
            return [value, toggle];
        }
    

State Management with Custom Hooks

Custom Hooks can manage state by encapsulating stateful logic and returning the state and any functions to update it. This allows you to reuse state management logic across multiple components.

Example:

        function useCounter(initialValue = 0) {
            const [count, setCount] = useState(initialValue);
            const increment = () => setCount(count + 1);
            const decrement = () => setCount(count - 1);
            return { count, increment, decrement };
        }
    

Data Fetching with Custom Hooks

Custom Hooks can handle data fetching by encapsulating the logic for making API requests and managing the loading and error states. This allows you to reuse data fetching logic across multiple components.

Example:

        function useFetch(url) {
            const [data, setData] = useState(null);
            const [loading, setLoading] = useState(true);
            const [error, setError] = useState(null);

            useEffect(() => {
                fetch(url)
                    .then(response => response.json())
                    .then(data => {
                        setData(data);
                        setLoading(false);
                    })
                    .catch(error => {
                        setError(error);
                        setLoading(false);
                    });
            }, [url]);

            return { data, loading, error };
        }
    

Form Handling with Custom Hooks

Custom Hooks can handle form state and validation by encapsulating the logic for managing form inputs and validation rules. This allows you to reuse form handling logic across multiple forms.

Example:

        function useForm(initialValues) {
            const [values, setValues] = useState(initialValues);
            const [errors, setErrors] = useState({});

            const handleChange = e => {
                setValues({ ...values, [e.target.name]: e.target.value });
            };

            const validate = () => {
                const newErrors = {};
                if (!values.email) newErrors.email = 'Email is required';
                if (!values.password) newErrors.password = 'Password is required';
                setErrors(newErrors);
                return Object.keys(newErrors).length === 0;
            };

            return { values, errors, handleChange, validate };
        }
    

Event Handling with Custom Hooks

Custom Hooks can handle event listeners by encapsulating the logic for adding and removing event listeners. This allows you to reuse event handling logic across multiple components.

Example:

        function useEventListener(eventName, handler, element = window) {
            useEffect(() => {
                element.addEventListener(eventName, handler);
                return () => {
                    element.removeEventListener(eventName, handler);
                };
            }, [eventName, handler, element]);
        }
    

Local Storage with Custom Hooks

Custom Hooks can handle local storage by encapsulating the logic for reading and writing data to local storage. This allows you to reuse local storage logic across multiple components.

Example:

        function useLocalStorage(key, initialValue) {
            const [storedValue, setStoredValue] = useState(() => {
                try {
                    const item = window.localStorage.getItem(key);
                    return item ? JSON.parse(item) : initialValue;
                } catch (error) {
                    return initialValue;
                }
            });

            const setValue = value => {
                try {
                    const valueToStore = value instanceof Function ? value(storedValue) : value;
                    setStoredValue(valueToStore);
                    window.localStorage.setItem(key, JSON.stringify(valueToStore));
                } catch (error) {
                    console.error(error);
                }
            };

            return [storedValue, setValue];
        }
    

Debouncing and Throttling with Custom Hooks

Custom Hooks can handle debouncing and throttling by encapsulating the logic for delaying function calls. This allows you to reuse debouncing and throttling logic across multiple components.

Example:

        function useDebounce(value, delay) {
            const [debouncedValue, setDebouncedValue] = useState(value);

            useEffect(() => {
                const handler = setTimeout(() => {
                    setDebouncedValue(value);
                }, delay);

                return () => {
                    clearTimeout(handler);
                };
            }, [value, delay]);

            return debouncedValue;
        }
    

Responsive Design with Custom Hooks

Custom Hooks can handle responsive design by encapsulating the logic for detecting screen size and orientation. This allows you to reuse responsive design logic across multiple components.

Example:

        function useWindowSize() {
            const [windowSize, setWindowSize] = useState({
                width: window.innerWidth,
                height: window.innerHeight,
            });

            useEffect(() => {
                const handleResize = () => {
                    setWindowSize({
                        width: window.innerWidth,
                        height: window.innerHeight,
                    });
                };

                window.addEventListener('resize', handleResize);
                return () => {
                    window.removeEventListener('resize', handleResize);
                };
            }, []);

            return windowSize;
        }
    

Authentication with Custom Hooks

Custom Hooks can handle authentication by encapsulating the logic for checking authentication status and managing user sessions. This allows you to reuse authentication logic across multiple components.

Example:

        function useAuth() {
            const [user, setUser] = useState(null);
            const [loading, setLoading] = useState(true);

            useEffect(() => {
                const checkAuth = async () => {
                    try {
                        const user = await fetchUser();
                        setUser(user);
                    } catch (error) {
                        setUser(null);
                    } finally {
                        setLoading(false);
                    }
                };

                checkAuth();
            }, []);

            return { user, loading };
        }
    

Error Handling with Custom Hooks

Custom Hooks can handle error handling by encapsulating the logic for catching and handling errors. This allows you to reuse error handling logic across multiple components.

Example:

        function useErrorHandler() {
            const [error, setError] = useState(null);

            const handleError = error => {
                setError(error);
                console.error(error);
            };

            return { error, handleError };
        }
    

Real-world Examples

Real-world examples of Custom Hooks include:

Analogies

Think of Custom Hooks as reusable recipes in a cookbook. Just as a recipe can be used to prepare a dish multiple times, a Custom Hook can be used to encapsulate logic that can be reused across multiple components. Each recipe (Custom Hook) can be applied to different dishes (components), making the process of adding functionality more efficient.

Another analogy is a toolbox. Just as a toolbox contains multiple tools that can be used for different tasks, Custom Hooks contain reusable logic that can be used for different components. Each tool (Custom Hook) can be applied to different tasks (components), making the process of adding functionality more efficient.