Understanding the Problem
In this practice, we need to:
- Update the Navbar component to consume and set the context value
- Monitor component rendering to understand when components re-render
- Create a new Match component that consumes context and has local state
- Understand how context changes affect component rendering
The key concept is learning how component re-rendering works with React Context, and understanding which components update when context changes.
Devising a Plan
- Add console.log to SideCard.jsx to track its rendering
- Update Navbar.jsx to consume the context and set the current sign on click
- Add console.log to Navbar.jsx to track its rendering
- Create a new Match.jsx component with local state
- Update Detail.jsx to include the Match component
- 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:
- Initially, all components render once during the first render
- 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
- 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:
- Theme switching: A theme context can be updated from a settings component, causing themed components to update
- User authentication: A user context can provide login state to components that need it
- Shopping carts: A cart context can be updated from product pages, and cart indicators can reflect the changes
The ability to selectively update only components that consume a context makes React applications more efficient and easier to maintain.