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
Advanced React Patterns

Advanced React Patterns

Key Concepts

Compound Components

Compound Components are a pattern where multiple components work together to form a single, cohesive unit. This pattern allows for more flexible and reusable components.

Example:

        function Accordion({ children }) {
            return <div>{children}</div>;
        }

        function AccordionItem({ title, children }) {
            return (
                <div>
                    <h3>{title}</h3>
                    <div>{children}</div>
                </div>
            );
        }

        function App() {
            return (
                <Accordion>
                    <AccordionItem title="Item 1">Content 1</AccordionItem>
                    <AccordionItem title="Item 2">Content 2</AccordionItem>
                </Accordion>
            );
        }
    

Higher-Order Components (HOC)

Higher-Order Components are functions that take a component and return a new component with additional props or behavior. This pattern is useful for sharing logic across multiple components.

Example:

        function withLoading(Component) {
            return function WithLoadingComponent({ isLoading, ...props }) {
                if (!isLoading) return <Component {...props} />;
                return <p>Loading...</p>;
            }
        }

        const EnhancedComponent = withLoading(MyComponent);
    

Render Props

Render Props is a pattern where a component's prop is a function that returns a React element. This allows for dynamic rendering and sharing of logic between components.

Example:

        function MouseTracker({ render }) {
            const [position, setPosition] = useState({ x: 0, y: 0 });

            const handleMouseMove = (event) => {
                setPosition({ x: event.clientX, y: event.clientY });
            };

            return (
                <div style={{ height: '100vh' }} onMouseMove={handleMouseMove}>
                    {render(position)}
                </div>
            );
        }

        function App() {
            return (
                <MouseTracker render={(position) => (
                    <h1>The mouse position is ({position.x}, {position.y})</h1>
                )}></MouseTracker>
            );
        }
    

Context API

The Context API allows you to pass data through the component tree without having to pass props down manually at every level. This is useful for global state management.

Example:

        const ThemeContext = React.createContext('light');

        function App() {
            return (
                <ThemeContext.Provider value="dark">
                    <Toolbar />
                </ThemeContext.Provider>
            );
        }

        function Toolbar() {
            return (
                <div>
                    <ThemedButton />
                </div>
            );
        }

        function ThemedButton() {
            const theme = useContext(ThemeContext);
            return <Button theme={theme} />;
        }
    

Custom Hooks

Custom Hooks allow you to extract component logic into reusable functions. This pattern promotes code reuse and separation of concerns.

Example:

        function useWindowSize() {
            const [size, setSize] = useState([0, 0]);
            useEffect(() => {
                const handleResize = () => {
                    setSize([window.innerWidth, window.innerHeight]);
                };
                window.addEventListener('resize', handleResize);
                return () => {
                    window.removeEventListener('resize', handleResize);
                };
            }, []);
            return size;
        }

        function App() {
            const [width, height] = useWindowSize();
            return (
                <div>
                    <p>Width: {width}</p>
                    <p>Height: {height}</p>
                </div>
            );
        }
    

Controlled Components

Controlled Components are components where form data is handled by the React component's state. This pattern ensures that the component has full control over the form elements.

Example:

        function ControlledInput() {
            const [value, setValue] = useState('');

            const handleChange = (event) => {
                setValue(event.target.value);
            };

            return (
                <input type="text" value={value} onChange={handleChange} />
            );
        }
    

Uncontrolled Components

Uncontrolled Components are components where form data is handled by the DOM itself. This pattern is useful for simpler forms where you don't need to control the input values.

Example:

        function UncontrolledInput() {
            const inputRef = useRef(null);

            const handleSubmit = (event) => {
                alert('A name was submitted: ' + inputRef.current.value);
                event.preventDefault();
            };

            return (
                <form onSubmit={handleSubmit}>
                    <label>
                        Name:
                        <input type="text" ref={inputRef} />
                    </label>
                    <input type="submit" value="Submit" />
                </form>
            );
        }
    

Provider Pattern

The Provider Pattern involves wrapping a component tree with a Provider component that supplies context values to its descendants. This pattern is useful for managing global state.

Example:

        const ThemeContext = React.createContext('light');

        function ThemeProvider({ children }) {
            const [theme, setTheme] = useState('light');
            return (
                <ThemeContext.Provider value={{ theme, setTheme }}>
                    {children}
                </ThemeContext.Provider>
            );
        }

        function App() {
            return (
                <ThemeProvider>
                    <Toolbar />
                </ThemeProvider>
            );
        }
    

State Reducer Pattern

The State Reducer Pattern allows you to externalize the state management logic by passing a reducer function to a component. This pattern is useful for customizing state updates.

Example:

        function Counter({ initialCount = 0, reducer = (state) => state }) {
            const [state, dispatch] = useReducer(reducer, { count: initialCount });

            return (
                <div>
                    Count: {state.count}
                    <button onClick={() => dispatch({ type: 'increment' })}>+</button>
                    <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
                </div>
            );
        }

        function customReducer(state, action) {
            switch (action.type) {
                case 'increment': return { count: state.count + 1 };
                case 'decrement': return { count: state.count - 1 };
                default: return state;
            }
        }

        function App() {
            return <Counter reducer={customReducer} />;
        }
    

State Initializer Pattern

The State Initializer Pattern allows you to pass an initial state to a component, which can be useful for resetting the state to its initial value.

Example:

        function Counter({ initialCount = 0 }) {
            const [count, setCount] = useState(initialCount);

            return (
                <div>
                    Count: {count}
                    <button onClick={() => setCount(initialCount)}>Reset</button>
                    <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
                    <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
                </div>
            );
        }
    

State Normalization

State Normalization involves organizing state in a way that avoids duplication and makes it easier to update. This pattern is useful for managing complex state structures.

Example:

        const initialState = {
            byId: {
                1: { id: 1, name: 'Item 1' },
                2: { id: 2, name: 'Item 2' },
            },
            allIds: [1, 2],
        };

        function reducer(state, action) {
            switch (action.type) {
                case 'ADD_ITEM':
                    return {
                        ...state,
                        byId: {
                            ...state.byId,
                            [action.payload.id]: action.payload,
                        },
                        allIds: [...state.allIds, action.payload.id],
                    };
                default:
                    return state;
            }
        }
    

State Persistence

State Persistence involves saving the state to a persistent storage like localStorage or sessionStorage. This pattern is useful for maintaining state across page reloads.

Example:

        function usePersistentState(key, initialState) {
            const [state, setState] = useState(() => {
                const storedState = localStorage.getItem(key);
                return storedState ? JSON.parse(storedState) : initialState;
            });

            useEffect(() => {
                localStorage.setItem(key, JSON.stringify(state));
            }, [key, state]);

            return [state, setState];
        }

        function App() {
            const [count, setCount] = usePersistentState('count', 0);

            return (
                <div>
                    Count: {count}
                    <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
                    <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
                </div>
            );
        }
    

Analogies

Think of Compound Components as a family of Lego blocks that fit together to build a complex structure. Higher-Order Components are like adding accessories to a toy, enhancing its functionality. Render Props are like a recipe where you can substitute ingredients to get different results. Context API is like a central air conditioning system that cools the entire house. Custom Hooks are like reusable tools in a toolbox. Controlled Components are like a remote-controlled toy, fully managed by the controller. Uncontrolled Components are like a toy that operates on its own. The Provider Pattern is like a power outlet that supplies electricity to all connected devices. The State Reducer Pattern is like a programmable thermostat that adjusts the temperature based on your preferences. The State Initializer Pattern is like a reset button on a game console. State Normalization is like organizing your closet by categories. State Persistence is like saving your game progress so you can resume later.