Context Wrapper - Dynamic Provider

Understanding the Problem

In this practice, we need to:

The goal is to understand how context provides a way to pass data through the component tree without having to pass props down manually at every level.

Devising a Plan

  1. Create a HoroscopeProvider component in HoroscopeContext.jsx
  2. Import and use the horoscope data in the provider
  3. Set up state in the provider to track the current sign
  4. Pass both the sign data and setter function in the context value
  5. Update main.jsx to use our new provider component
  6. Update Detail.jsx to properly consume the context
  7. Test by changing the default state value

Carrying Out the Plan

Step 1: Create the HoroscopeProvider component

First, we'll modify HoroscopeContext.jsx to include our provider component:

// src/context/HoroscopeContext.jsx
import { createContext, useState } from 'react';
import horoscopesObj from '../data/horoscopes';

export const HoroscopeContext = createContext();

const HoroscopeProvider = props => {
  const [currentSign, setCurrentSign] = useState("Leo");
  
  const sign = horoscopesObj[currentSign];
  
  return (
    <HoroscopeContext.Provider value={{ sign, setCurrentSign }}>
      {props.children}
    </HoroscopeContext.Provider>
  );
};

export default HoroscopeProvider;

In this code:

Step 2: Update main.jsx

Next, we need to update main.jsx to use our new provider component:

// src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import HoroscopeProvider from './context/HoroscopeContext';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <HoroscopeProvider>
      <App />
    </HoroscopeProvider>
  </React.StrictMode>
);

Step 3: Update Detail.jsx to consume the context

Now we need to update Detail.jsx to properly consume our context:

// src/components/Detail.jsx
import { useContext } from 'react';
import { HoroscopeContext } from '../context/HoroscopeContext';

const Detail = () => {
  const { sign } = useContext(HoroscopeContext);

  return (
    <div className='details'>
      <img
        src={sign.backgroundImg}
        alt={sign.name}
      />
      <h2>{sign.name}</h2>
      <h4>Element: {sign.element}</h4>
      <h4>Traits: {sign.traits}</h4>
    </div>
  );
};

export default Detail;

In this component:

Testing Our Solution

To test our solution, we can:

  1. Run the application with npm run dev
  2. Verify that the Detail component displays the Leo horoscope information
  3. Change the default state in HoroscopeProvider to another sign (e.g., "Virgo")
  4. Verify that the Detail component now displays the new sign's information

Extra: Making the Navbar Interactive

While not part of the current assignment, a natural next step would be to make the Navbar interactive, so clicking on a sign updates the current sign in our context:

// src/components/Navbar.jsx
import { useContext } from 'react';
import horoscopeObj from '../data/horoscopes';
import { HoroscopeContext } from '../context/HoroscopeContext';

const Navbar = () => {
  const { setCurrentSign } = useContext(HoroscopeContext);
  const horoscopes = Object.keys(horoscopeObj);
  
  return (
    <nav>
      {horoscopes.map(sign => (
        <span 
          key={sign}
          onClick={() => setCurrentSign(sign)}
        >
          {sign}
        </span>
      ))}
    </nav>
  );
};

export default Navbar;

Understanding the Concept

What is React Context?

React Context provides a way to share data between components without having to explicitly pass props through every level of the tree. It's especially useful for sharing global data like:

Context Components

Real-world Analogy

Think of React Context like a family's thermostat setting. The thermostat is set in one place (the Provider), but the temperature affects everyone in the house (the Consumers), without having to tell each person individually what the temperature is.

Why We Need a Provider Component

Creating a separate Provider component (instead of just using Context.Provider directly) gives us these advantages: