Context Dynamic Consumer

Understanding the Problem

In this practice, we need to:

The key concept is learning how component re-rendering works with React Context, and understanding which components update when context changes.

Devising a Plan

  1. Add console.log to SideCard.jsx to track its rendering
  2. Update Navbar.jsx to consume the context and set the current sign on click
  3. Add console.log to Navbar.jsx to track its rendering
  4. Create a new Match.jsx component with local state
  5. Update Detail.jsx to include the Match component
  6. Test the application and observe rendering behavior

Carrying Out the Plan

Step 1: Add console.log to SideCard.jsx

First, we'll modify the SideCard component to add a console.log:

// src/components/SideCard.jsx
const SideCard = () => {
  console.log('SideCard is rendering!');
  
  return (
    <div className='side-card'>
      <h1>React Context with Horoscopes</h1>
    </div>
  );
};

export default SideCard;

Step 2: Update Navbar.jsx to consume and set context

Next, let's update the Navbar component to use the context:

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

const Navbar = () => {
  console.log('Navbar is rendering!');
  
  const { setCurrentSign } = useContext(HoroscopeContext);
  const horoscopes = Object.keys(horoscopesObj);

  return (
    <nav>
      {horoscopes.map(sign => (
        <span 
          key={sign}
          onClick={() => setCurrentSign(sign)}
        >
          {sign}
        </span>
      ))}
    </nav>
  )
};

export default Navbar;

Step 3: Create Match.jsx component

Now, let's create the Match component with local state:

// src/components/Match.jsx
import { useState, useContext } from 'react';
import { HoroscopeContext } from '../context/HoroscopeContext';

const Match = () => {
  console.log('Match is rendering!');
  
  const [match, setMatch] = useState(false);
  const { sign } = useContext(HoroscopeContext);
  
  return (
    <div>
      <button onClick={() => setMatch(!match)}>
        {match ? 'Hide Match' : 'Show Match'}
      </button>
      
      {match && (
        <div>
          <h4>Your match is: {sign.match}</h4>
        </div>
      )}
    </div>
  );
};

export default Match;

Step 4: Update Detail.jsx to include Match component

Finally, let's update the Detail component to include our new Match component:

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

const Detail = () => {
  console.log('Detail is rendering!');
  
  const { sign } = useContext(HoroscopeContext);

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

export default Detail;

Looking Back and Understanding

How It Works

Let's break down what happens when the application runs:

  1. Initially, all components render once during the first render
  2. When you click on a sign in the Navbar:
    • The Navbar's onClick handler calls setCurrentSign(sign)
    • This updates the context value in HoroscopeContext.jsx
    • Any component consuming the context re-renders
  3. Component behavior:
    • Navbar re-renders because it consumes the context (even though it doesn't display the current sign)
    • Detail re-renders because it consumes the context to display the sign's details
    • Match re-renders because it consumes the context, but its local state (match) is preserved
    • SideCard does not re-render because it doesn't consume the context

Key Concepts

Context Consumption: Only components that actually use useContext with the specific context will re-render when that context changes.

Local State Preservation: Even when components re-render due to context changes, their local state is preserved. This is why the Match component's "match" state doesn't reset when clicking on different signs.

Performance Optimization: Components that don't consume the context (like SideCard) don't waste resources re-rendering when the context changes.

Real-World Application

This pattern is commonly used in larger applications for features like:

The ability to selectively update only components that consume a context makes React applications more efficient and easier to maintain.