Understanding the .HardWrap
Component
What is HardWrap?
.HardWrap
is a special version of .Wrap
that protects your components from unnecessary re-renders. Think of it as a "shield" that keeps your component stable when its parent component updates.
const sMySignify = signify("Hello World");
// This component will re-render every time its parent re-renders
<sMySignify.Wrap>
{value => <div>{value}</div>}
</sMySignify.Wrap>
// This component will ONLY re-render when sMySignify's value changes
<sMySignify.HardWrap>
{value => <div>{value}</div>}
</sMySignify.HardWrap>
Why Do We Need HardWrap?
In React, when a parent component re-renders, all its children re-render too. This can cause performance problems:
function ParentComponent() {
const [counter, setCounter] = useState(0);
return (
<div>
<button onClick={() => setCounter(counter + 1)}>
Click me: {counter}
</button>
{/* This expensive component re-renders every time you click the button! */}
<mySignify.Wrap>
{(value) => <ExpensiveComponent value={value} />}
</mySignify.Wrap>
</div>
);
}
The problem: Even though ExpensiveComponent
doesn't need the counter
value, it still re-renders when you click the button.
The solution: Use .HardWrap
to prevent this:
function ParentComponent() {
const [counter, setCounter] = useState(0);
return (
<div>
<button onClick={() => setCounter(counter + 1)}>
Click me: {counter}
</button>
{/* Now ExpensiveComponent only re-renders when mySignify changes! */}
<mySignify.HardWrap>
{(value) => <ExpensiveComponent value={value} />}
</mySignify.HardWrap>
</div>
);
}
Step-by-Step Comparison: Wrap vs HardWrap
Let's see the difference with a simple example:
const sMyCounter = signify(0);
function App() {
const [appState, setAppState] = useState(0);
return (
<div>
{/* This button updates the parent component */}
<button onClick={() => setAppState(appState + 1)}>
Update App: {appState}
</button>
{/* This button updates the signify value */}
<button onClick={() => sMyCounter.set(sMyCounter.value + 1)}>
Update Counter: {sMyCounter.value}
</button>
{/* Regular Wrap - will re-render when EITHER button is clicked */}
<sMyCounter.Wrap>
{(count) => {
console.log("Wrap rendered!"); // This logs on both button clicks
return <div>Count: {count}</div>;
}}
</sMyCounter.Wrap>
{/* HardWrap - will ONLY re-render when counter button is clicked */}
<sMyCounter.HardWrap>
{(count) => {
console.log("HardWrap rendered!"); // This only logs when counter changes
return <div>Count: {count}</div>;
}}
</sMyCounter.HardWrap>
</div>
);
}
What Happens When You Click Each Button?
Button Clicked | .Wrap Behavior | .HardWrap Behavior |
---|---|---|
"Update App" | ✅ Re-renders (unnecessary) | ❌ Does NOT re-render |
"Update Counter" | ✅ Re-renders (necessary) | ✅ Re-renders (necessary) |
Key Point: .HardWrap
is "smarter" - it only re-renders when it actually needs to!
The Magic Result ✨
- Parent re-renders → HardWrap stays calm 😴
- Signify value changes → HardWrap wakes up and updates 🚀
When Should You Use HardWrap?
1. Slow/Expensive Components 🐌
If you have a component that takes time to render (like charts, large lists, or complex calculations), use HardWrap to protect it:
const sChartData = signify([]);
function ExpensiveChart({ data }) {
// This component takes 100ms+ to render - it's expensive!
const processedData = useMemo(() => {
return data.map(item => /* complex calculation */);
}, [data]);
return <ComplexChartLibrary data={processedData} />;
}
function Dashboard() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div>
{/* This button re-renders the Dashboard frequently */}
<button onClick={() => setSidebarOpen(!sidebarOpen)}>
Toggle Sidebar
</button>
{/* Without HardWrap: ExpensiveChart re-renders every time you toggle sidebar! */}
<sChartData.Wrap>
{data => <ExpensiveChart data={data} />}
</sChartData.Wrap>
{/* With HardWrap: ExpensiveChart only re-renders when sChartData changes */}
<sChartData.HardWrap>
{data => <ExpensiveChart data={data} />}
</sChartData.HardWrap>
</div>
);
}
Why this helps: The expensive chart only re-renders when the actual data changes, not when the sidebar toggles!
2. Components That Update Frequently ⚡
When your parent component updates often (like real-time dashboards), use HardWrap to keep other parts stable:
const sLiveData = signify([]);
function LiveDashboard() {
const [currentTime, setCurrentTime] = useState(new Date());
// This updates every second, causing parent to re-render
useEffect(() => {
const timer = setInterval(() => {
setCurrentTime(new Date());
}, 1000);
return () => clearInterval(timer);
}, []);
return (
<div>
{/* This updates every second */}
<div>Current time: {currentTime.toLocaleTimeString()}</div>
{/* Without HardWrap: This list re-renders every second even if data didn't change! */}
<sLiveData.Wrap>
{(data) => (
<ul>
{data.map((item) => (
<li key={item.id}>{item.message}</li>
))}
</ul>
)}
</sLiveData.Wrap>
{/* With HardWrap: This list only re-renders when new data arrives */}
<sLiveData.HardWrap>
{(data) => (
<ul>
{data.map((item) => (
<li key={item.id}>{item.message}</li>
))}
</ul>
)}
</sLiveData.HardWrap>
</div>
);
}
Why this helps: The data list doesn't flicker or re-render unnecessarily just because the time updates!
3. Third-Party Libraries 📚
When using external libraries (like chart libraries, maps, editors), these often take time to render and should be protected:
const sChartData = signify([]);
function Dashboard() {
const [activeTab, setActiveTab] = useState("overview");
return (
<div>
{/* Tab switching causes parent re-renders */}
<button onClick={() => setActiveTab("overview")}>Overview</button>
<button onClick={() => setActiveTab("details")}>Details</button>
{/* Third-party chart library is expensive to re-render */}
<sChartData.HardWrap>
{(data) => (
<HighchartsReact
highcharts={Highcharts}
options={{
series: [{ data }],
chart: { type: "line" },
}}
/>
)}
</sChartData.HardWrap>
</div>
);
}
Why this helps: The chart library doesn't have to re-initialize and re-render when you switch tabs!
Quick Decision Guide: When to Use HardWrap?
✅ Use HardWrap when:
- Your component is slow or expensive to render
- Your component uses third-party libraries (charts, maps, etc.)
- Your parent component re-renders frequently
- You have animations that shouldn't be interrupted
- You're rendering large lists or complex data
❌ Don't use HardWrap when:
- Your component is simple (just text or basic elements)
- You need data from the parent component
- You're still developing/debugging (regular Wrap is easier to debug)
- The component rarely re-renders anyway
Simple Rule of Thumb 🎯
Ask yourself: "Is this component slow or does it re-render too often?"
- Yes → Use
.HardWrap
- No → Use regular
.Wrap
or.use()
Real Performance Example
Here's a practical example showing the performance difference:
const sTodoList = signify([]);
function TodoApp() {
const [searchTerm, setSearchTerm] = useState("");
return (
<div>
{/* This search input causes parent re-renders on every keystroke */}
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search todos..."
/>
{/* ❌ Regular Wrap: sTodoList re-renders on every keystroke! */}
<sTodoList.Wrap>
{(todos) => <sTodoList todos={todos} />} {/* Slow re-renders */}
</sTodoList.Wrap>
{/* ✅ HardWrap: sTodoList only re-renders when todos actually change */}
<sTodoList.HardWrap>
{(todos) => <sTodoList todos={todos} />} {/* Fast and stable */}
</sTodoList.HardWrap>
</div>
);
}
Result: With HardWrap, typing in the search box feels smooth because the todo list doesn't re-render unnecessarily!
Common Patterns & Tips
Multiple HardWraps in One Component
You can use multiple HardWraps to optimize different parts independently:
const sUserData = signify(null);
const sNotifications = signify([]);
function UserDashboard() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div>
<button onClick={() => setSidebarOpen(!sidebarOpen)}>
Toggle Sidebar
</button>
{/* Each section is protected independently */}
<sUserData.HardWrap>
{(user) => (
<div>
<h1>Welcome, {user?.name}</h1>
<p>{user?.email}</p>
</div>
)}
</sUserData.HardWrap>
<sNotifications.HardWrap>
{(notifs) => (
<div>
<h3>Notifications ({notifs.length})</h3>
{notifs.map((notif) => (
<div key={notif.id}>{notif.message}</div>
))}
</div>
)}
</sNotifications.HardWrap>
</div>
);
}
Mixing HardWrap with Regular Components
You don't have to use HardWrap everywhere - mix and match based on need:
function SmartApp() {
const [uiState, setUIState] = useState("loading");
return (
<div>
{/* Simple text - no need for HardWrap */}
<h1>My App</h1>
{/* Simple state display - regular state is fine */}
<div>Status: {uiState}</div>
{/* Expensive component - use HardWrap */}
<expensiveData.HardWrap>
{(data) => <ExpensiveChart data={data} />}
</expensiveData.HardWrap>
</div>
);
}
Testing Your HardWrap Components
Simple Test to Verify It's Working
Add console logs to see when your components re-render:
function TestComponent() {
const [trigger, setTrigger] = useState(0);
return (
<div>
<button onClick={() => setTrigger(trigger + 1)}>
Cause Parent Re-render: {trigger}
</button>
<myData.HardWrap>
{(data) => {
console.log("HardWrap rendered!"); // Should only log when myData changes
return <ExpensiveComponent data={data} />;
}}
</myData.HardWrap>
</div>
);
}
Expected behavior: The console log should only appear when myData
changes, not when you click the button!
Troubleshooting
"My HardWrap is still re-rendering!"
Check these common issues:
Are you accessing parent state inside HardWrap?
tsx// ❌ This won't work - you're accessing parent state function Parent() { const [parentData, setParentData] = useState("hello"); return ( <mySignify.HardWrap> {(data) => <Component data={data} parentData={parentData} />} </mySignify.HardWrap> ); }
Are you creating new objects/functions inside the parent?
tsx// ❌ This creates a new function on every render <mySignify.HardWrap> {(data) => ( <Component data={data} onClick={() => console.log("clicked")} /> )} </mySignify.HardWrap>; // ✅ Move the function outside or use useCallback const handleClick = useCallback(() => console.log("clicked"), []); <mySignify.HardWrap> {(data) => <Component data={data} onClick={handleClick} />} </mySignify.HardWrap>;
Summary
What is HardWrap? A performance optimization that prevents unnecessary re-renders from parent components.
When should I use it?
- When your component is slow/expensive
- When your parent re-renders frequently
- When using third-party libraries
When should I NOT use it?
- For simple components (just text, basic elements)
- When you need data from the parent component
- When you're still learning/debugging
How to use it? Just replace .Wrap
with .HardWrap
:
// Before
<mySignify.Wrap>
{data => <MyComponent data={data} />}
</mySignify.Wrap>
// After
<mySignify.HardWrap>
{data => <MyComponent data={data} />}
</mySignify.HardWrap>
Remember: HardWrap is an optimization tool. Start with regular .Wrap
and only switch to .HardWrap
when you actually have performance issues!
💡 Beginner Tip: Don't worry about HardWrap until your app feels slow. Focus on learning the basics first, then optimize later when needed.