React Context with Horoscopes

Understanding the Problem

In this tutorial, we need to implement React Context in a horoscope application. We need to:

  1. Create a Context using createContext
  2. Set up a Provider to wrap our application
  3. Consume the context in the Detail component

This will allow us to share data (horoscope information) across components without prop drilling.

Devising a Plan

  1. Create a context directory and file for our HoroscopeContext
  2. Create the context using React's createContext
  3. Wrap our App with the HoroscopeContext.Provider in main.jsx
  4. Pass horoscope data through the Provider's value prop
  5. Consume the context in our Detail component using useContext
  6. Display the horoscope information in the Detail component

Carrying Out the Plan

Step 1: Create the Context File

First, we need to create a directory called context in the src folder and create a file called HoroscopeContext.jsx inside it.

File location: src/context/HoroscopeContext.jsx


import { createContext } from 'react';

export const HoroscopeContext = createContext();
            

This creates our context using React's createContext function. Think of this as creating a special channel through which data can flow across your component tree.

Step 2: Set Up the Provider in main.jsx

Now, we need to modify main.jsx to import our context and wrap the App component with the Provider.

File location: src/main.jsx


import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import { HoroscopeContext } from './context/HoroscopeContext';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <HoroscopeContext.Provider value={{ sign: "Leo" }}>
      <App />
    </HoroscopeContext.Provider>
  </React.StrictMode>
);
            

Here, we're wrapping our entire App with the HoroscopeContext.Provider and providing an initial value of { sign: "Leo" }. This is like setting up a broadcast station that will make this data available to any component that wants to tune in.

Step 3: Consume the Context in the Detail Component

Now, let's modify the Detail component to consume our context.

File location: src/components/Detail.jsx


import { useContext } from 'react';
import { HoroscopeContext } from '../context/HoroscopeContext';

const Detail = () => {
  const horoscopesObj = useContext(HoroscopeContext);
  
  return (
    <div className='details'>
      <img
        src='https://upload.wikimedia.org/wikipedia/commons/e/e1/FullMoon2010.jpg'
        alt=''
      />
      <h2>{horoscopesObj.sign}</h2>
      <h4>Element: </h4>
      <h4>Traits: </h4>
    </div>
  );
};

export default Detail;
            

In this step, we import useContext from React and our HoroscopeContext. We then use the useContext hook to access the context value. This is like tuning in to receive the broadcast from our Provider.

We display the sign from our context in the h2 element, replacing "Current Sign Name" with the actual sign (Leo).

Looking Back and Extending

We've successfully implemented React Context in our horoscope application! Let's review what we've done:

  1. Created a context using createContext
  2. Set up a Provider to wrap our application
  3. Consumed the context in the Detail component

Further Improvements

Now that we have the basic context set up, here are some ways we could improve our application:

Display More Horoscope Information

We can enhance our context to include more information from our horoscope data.

Updated main.jsx:


import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import { HoroscopeContext } from './context/HoroscopeContext';
import horoscopeObj from './data/horoscopes';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <HoroscopeContext.Provider value={{ 
      sign: "Leo",
      currentHoroscope: horoscopeObj["Leo"]
    }}>
      <App />
    </HoroscopeContext.Provider>
  </React.StrictMode>
);
            

Updated Detail.jsx:


import { useContext } from 'react';
import { HoroscopeContext } from '../context/HoroscopeContext';

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

export default Detail;
            

Implement Sign Selection

We could also implement functionality to change the selected sign when clicking on the navbar items. This would require using React's useState hook alongside our context.

Create a Custom Provider Component

For more complex state management, we might want to create a custom Provider component:


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

export const HoroscopeContext = createContext();

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

And then in 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>
);
            

Real-World Applications

React Context is widely used in real-world applications for scenarios like:

Context API is especially useful for data that is considered "global" for a tree of React components and when you want to avoid "prop drilling" (passing props through many levels of components).

However, for complex state management with frequent updates, you might want to consider using more specialized state management libraries like Redux or Zustand alongside Context.