# useContext

React, one of the most popular JavaScript libraries for building user interfaces, offers various hooks that allow developers to manage state and side effects in functional components. Among these hooks, `useContext` stands out for its ability to manage and share state across multiple components without prop drilling. In this extensive guide, we'll dive deep into the `useContext` hook, understand its utility, explore its usage through examples, and discuss best practices.

### Table of Contents

1. Introduction to Context API
2. What is the `useContext` Hook?
3. Setting Up Context in a React Application
4. Using `useContext` in Functional Components
5. Practical Examples
   * Example 1: Theme Switcher
   * Example 2: User Authentication
   * Example 3: Multi-language Support
6. Best Practices for Using `useContext`
7. Common Pitfalls and How to Avoid Them
8. Conclusion

### 1. Introduction to Context API

The Context API in React is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language. By using Context, you can avoid passing props down manually at every level of your application.

#### Key Components of Context API:

* **React.createContext**: Creates a Context object.
* **Provider**: A component that provides the value to its children.
* **Consumer**: A component that subscribes to context changes (used before hooks).

### 2. What is the `useContext` Hook?

The `useContext` hook simplifies the process of consuming context. It allows you to subscribe to React context without needing a Consumer component.

#### Syntax:

```javascript
const value = useContext(MyContext);
```

### 3. Setting Up Context in a React Application

Before using `useContext`, you need to set up your context.

#### Step-by-Step Guide:

1. **Create a Context**:

   ```javascript
   import React, { createContext, useState } from 'react';

   const MyContext = createContext();
   ```
2. **Create a Provider Component**:

   ```javascript
   const MyProvider = ({ children }) => {
       const [state, setState] = useState(initialState);

       return (
           <MyContext.Provider value={{ state, setState }}>
               {children}
           </MyContext.Provider>
       );
   };
   ```
3. **Wrap Your Application with the Provider**:

   ```javascript
   import React from 'react';
   import ReactDOM from 'react-dom';
   import App from './App';
   import { MyProvider } from './MyContext';

   ReactDOM.render(
       <MyProvider>
           <App />
       </MyProvider>,
       document.getElementById('root')
   );
   ```

### 4. Using `useContext` in Functional Components

Now that the context is set up, you can use the `useContext` hook in any functional component to access the context value.

#### Example:

```javascript
import React, { useContext } from 'react';
import { MyContext } from './MyContext';

const MyComponent = () => {
    const { state, setState } = useContext(MyContext);

    return (
        <div>
            <p>Current State: {state}</p>
            <button onClick={() => setState('New State')}>Change State</button>
        </div>
    );
};

export default MyComponent;
```

### 5. Practical Examples

#### Example 1: Theme Switcher

**Context Setup:**

```javascript
import React, { createContext, useState } from 'react';

const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
    const [theme, setTheme] = useState('light');

    const toggleTheme = () => {
        setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
    };

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    );
};

export { ThemeProvider, ThemeContext };
```

**Consuming Context:**

```javascript
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

const ThemeSwitcher = () => {
    const { theme, toggleTheme } = useContext(ThemeContext);

    return (
        <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
            <p>Current Theme: {theme}</p>
            <button onClick={toggleTheme}>Toggle Theme</button>
        </div>
    );
};

export default ThemeSwitcher;
```

#### Example 2: User Authentication

**Context Setup:**

```javascript
import React, { createContext, useState } from 'react';

const AuthContext = createContext();

const AuthProvider = ({ children }) => {
    const [user, setUser] = useState(null);

    const login = (userData) => {
        setUser(userData);
    };

    const logout = () => {
        setUser(null);
    };

    return (
        <AuthContext.Provider value={{ user, login, logout }}>
            {children}
        </AuthContext.Provider>
    );
};

export { AuthProvider, AuthContext };
```

**Consuming Context:**

```javascript
import React, { useContext } from 'react';
import { AuthContext } from './AuthContext';

const LoginButton = () => {
    const { login } = useContext(AuthContext);

    return <button onClick={() => login({ name: 'John Doe' })}>Login</button>;
};

const UserProfile = () => {
    const { user, logout } = useContext(AuthContext);

    if (!user) {
        return <p>Please login</p>;
    }

    return (
        <div>
            <p>Welcome, {user.name}</p>
            <button onClick={logout}>Logout</button>
        </div>
    );
};

const App = () => (
    <div>
        <LoginButton />
        <UserProfile />
    </div>
);

export default App;
```

#### Example 3: Multi-language Support

**Context Setup:**

```javascript
import React, { createContext, useState } from 'react';

const LanguageContext = createContext();

const LanguageProvider = ({ children }) => {
    const [language, setLanguage] = useState('en');

    const switchLanguage = (lang) => {
        setLanguage(lang);
    };

    return (
        <LanguageContext.Provider value={{ language, switchLanguage }}>
            {children}
        </LanguageContext.Provider>
    );
};

export { LanguageProvider, LanguageContext };
```

**Consuming Context:**

```javascript
import React, { useContext } from 'react';
import { LanguageContext } from './LanguageContext';

const LanguageSwitcher = () => {
    const { language, switchLanguage } = useContext(LanguageContext);

    return (
        <div>
            <p>Current Language: {language}</p>
            <button onClick={() => switchLanguage('en')}>English</button>
            <button onClick={() => switchLanguage('es')}>Spanish</button>
        </div>
    );
};

export default LanguageSwitcher;
```

### 6. Best Practices for Using `useContext`

1. **Keep Contexts Small**: Use context for specific, tightly-related data to avoid unnecessary re-renders.
2. **Combine Contexts**: If you have multiple contexts, consider combining them in a single component to minimize the number of providers.
3. **Memoize Values**: Use `useMemo` to memoize context values to prevent re-renders.

   ```javascript
   const value = useMemo(() => ({ state, setState }), [state]);
   ```
4. **Separate State Management**: Use context for state sharing, but handle state logic and updates within individual components or custom hooks.

### 7. Common Pitfalls and How to Avoid Them

* **Unnecessary Re-renders**: Avoid updating context values in a way that causes frequent re-renders of all components consuming the context.
* **Overusing Context**: Not all state should be placed in context. Use it for state that truly needs to be global.
* **Default Values**: Always provide sensible default values for your contexts to avoid undefined errors.

### 8. Conclusion

The `useContext` hook is a powerful tool in React that simplifies the process of sharing state across components. By understanding its fundamentals, setting up context properly, and following best practices, you can enhance the maintainability and scalability of your React applications.

We hope this comprehensive guide has provided you with a solid understanding of the `useContext` hook and how to leverage it effectively in your projects.
