Skip to content

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.

tsx
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:

tsx
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:

tsx
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:

tsx
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:

tsx
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:

tsx
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:

tsx
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:

tsx
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:

tsx
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:

tsx
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:

tsx
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:

  1. 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>
      );
    }
  2. 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:

tsx
// 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.

Released under the MIT License.