React
RE05

RE05 Exercises

Practical Exercises

  1. Using React Context

Objective: Create and use a React context to manage and share state across components.

Task:

Create a UserContext with createContext. Create a UserProvider component that uses the context provider to pass a user object and a function to update it. Consume the context in a child component using the useContext hook.

import React, { createContext, useContext, useState } from 'react';
 
interface User {
    name: string;
    age: number;
}
 
const UserContext = createContext<{ user: User | null; setUser: React.Dispatch<React.SetStateAction<User | null>> }>({ user: null, setUser: () => {} });
 
const UserProvider: React.FC = ({ children }) => {
    const [user, setUser] = useState<User | null>(null);
 
    return (
        <UserContext.Provider value={{ user, setUser }}>
            {children}
        </UserContext.Provider>
    );
};
 
const ChildComponent: React.FC = () => {
    const { user, setUser } = useContext(UserContext);
    
    // Example usage of context
    return <div>{user ? `Hello, ${user.name}` : 'No user'}</div>;
};
 
const ParentComponent: React.FC = () => (
    <UserProvider>
        <ChildComponent />
    </UserProvider>
);
 
export default ParentComponent;
  1. Managing State with useReducer

Objective: Use the useReducer hook to manage complex state.

Task:

Create a reducer function for managing the state of a counter (increment and decrement actions). Use useReducer in a component to manage the counter state and dispatch actions.

import React, { useReducer } from 'react';
 
type CounterState = { count: number };
type CounterAction = { type: 'increment' | 'decrement' };
 
const counterReducer = (state: CounterState, action: CounterAction): CounterState => {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        default:
            return state;
    }
};
 
const CounterComponent: React.FC = () => {
    const [state, dispatch] = useReducer(counterReducer, { count: 0 });
 
    return (
        <div>
            Count: {state.count}
            <button onClick={() => dispatch({ type: 'increment' })}>+</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        </div>
    );
};
 
export default CounterComponent;
  1. Combining Context and Reducers

Objective: Integrate context with reducers to manage complex state across multiple components.

Task:

Create a context to share a state managed by a reducer. Implement a provider component that uses useReducer. Consume the shared state and dispatch actions in child components.

import React, { createContext, useContext, useReducer } from 'react';
 
type State = { count: number };
type Action = { type: 'increment' | 'decrement' };
type Dispatch = (action: Action) => void;
 
const CountContext = createContext<{ state: State; dispatch: Dispatch } | undefined>(undefined);
 
const countReducer = (state: State, action: Action): State => {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        default:
            throw new Error('Unhandled action');
    }
};
 
const CountProvider: React.FC = ({ children }) => {
    const [state, dispatch] = useReducer(countReducer, { count: 0 });
    return <CountContext.Provider value={{ state, dispatch }}>{children}</CountContext.Provider>;
};
 
const useCount = () => {
    const context = useContext(CountContext);
    if (!context) {
        throw new Error('useCount must be used within a CountProvider');
    }
    return context;
};
 
const CounterButton: React.FC = () => {
    const { dispatch } = useCount();
    return <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>;
};
 
const CounterDisplay: React.FC = () => {
    const { state } = useCount();
    return <div>Count: {state.count}</div>;
};
 
const App: React.FC = () => (
    <CountProvider>
        <CounterDisplay />
        <CounterButton />
    </CountProvider>
);
 
export default App;