Skip to content

Make component keys configurable to optimize dynamic rendering#806

Merged
devnatan merged 17 commits intomainfrom
feat/component-keys
Nov 27, 2025
Merged

Make component keys configurable to optimize dynamic rendering#806
devnatan merged 17 commits intomainfrom
feat/component-keys

Conversation

@devnatan
Copy link
Copy Markdown
Owner

@devnatan devnatan commented Nov 26, 2025

Component keys are used to identify components and decide whether they should be re-rendered when a view-wide, context-wide, slot or component update occurs.

Previously, components always generated a random internal key (a UUID). This meant that on every full update, all dynamic components were treated as changed — forcing a re-render even when nothing relevant had changed.

Current Behavior (Before This PR)

In the example below:

  1. counterState is incremented every time the scheduled update triggers.
  2. Because scheduled updates call the global update, the component always re-renders.
  3. This happens because the item is a Dynamic Component (explained later) and its internal key is always a new random UUID.
private final MutableIntState counterState = mutableState(1);

@Override
public void onInit(@NotNull ViewConfigBuilder config) {
    config.title("Auto update (?)")
        .scheduleUpdate(10);
}

@Override
public void onFirstRender(@NotNull RenderContext render) {
    render.firstSlot().renderWith(() -> new ItemStack(
            /* type = */   Material.DIAMOND, 
            /* amount = */ counterState.get(render)
    ));
}

@Override
public void onUpdate(@NotNull Context update) {
    final int count = counterState.increment(update);
    update.updateTitleForPlayer(String.format("Auto update (%d)", count));
}

New Behavior (After This PR)

Component keys can now be explicitly defined using identifiedBy.

Although they can also be used for lookup (e.g., via Refs API), their primary purpose is to control whether a component should be re-rendered.

Inventory Framework considers a component dynamic if:

  1. It has visibility modifiers (e.g. displayIf, hideIf),
  2. It listens to state changes via updateOnStateChange, or
  3. It uses dynamic item factories such as onRender, renderWith, or withItem(() -> ...).

Dynamic components are evaluated during rendering to decide whether they should update. One of the final checks is the component key: if it changes, the component re-renders; if not, it stays as-is.

Before this PR the internal key was always random, so dynamic components were always treated as changed, causing unnecessary re-renders.

With identifiedBy, you can now stabilize that key:

render.firstSlot().renderWith(() -> new ItemStack(
    /* type = */ Material.DIAMOND,
    /* amount = */ counterState.get(render)
))
.identifiedBy("non-changing-key"); # <-- Look here

In this case, even though the scheduled update fires, the component will only re-render if its key changes.




Making the Component Re-Render Explicitly

If you want the previous behavior (always re-render), simply provide a changing key or do not provide any key:

render.firstSlot().renderWith(() -> new ItemStack(
    /* type = */ Material.DIAMOND,
    /* amount = */ counterState.get(render)
))
.identifiedBy(() -> {
    final String randomKey = "random-key-%d";
    return String.format(randomKey, ThreadLocalRandom.current().nextInt());
});

This mimics the old random-UUID behavior, but now you have full control over when the key changes — and therefore when the component re-renders.

See Scheduled Updates.

@devnatan devnatan self-assigned this Nov 26, 2025
@devnatan devnatan marked this pull request as ready for review November 26, 2025 16:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces explicit component keys to control re-rendering behavior during updates. Previously, all components used random UUIDs as keys, causing them to re-render on every update. Now, components can be assigned explicit keys (constant or dynamic) to optimize rendering by skipping updates when keys haven't changed.

  • Added identifiedBy() methods to ComponentBuilder for setting explicit component keys
  • Modified component rendering logic to use key comparison for determining update necessity
  • Updated IFSlotRenderContext creation to include component reference for key evaluation

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java Added three experimental identifiedBy() method signatures for setting component keys
inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java Replaced constant UUID key with dynamic keyFactory function and lastKey tracking; updated render() and updated() logic to use key comparison
inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java Added getComponent() method to expose component reference in slot contexts
inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java Removed getComponent() method (moved to parent interface IFSlotContext)
inventory-framework-api/src/main/java/me/devnatan/inventoryframework/internal/ElementFactory.java Updated createSlotRenderContext() signature to accept Component parameter
inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java Implemented identifiedBy() methods and added RANDOM_KEY_FACTORY constant for default key generation
inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/AbstractIFContext.java Updated slot render context creation to pass component reference
inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java Added keyFactory parameter handling and default to RANDOM_KEY_FACTORY when null
inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotRenderContext.java Added component field and getComponent() implementation
inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/internal/BukkitElementFactory.java Updated createSlotRenderContext() to pass component parameter
inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt Added keyFactory parameter handling with Kotlin syntax, converted constructor parameters to named arguments
inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt Added _component parameter and getComponent() implementation
inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt Updated createSlotRenderContext() to pass component parameter
inventory-framework-platform/src/test/java/me/devnatan/inventoryframework/component/TestItemComponentBuilder.java Added keyFactory parameter to test mock builder
inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java Updated mock createSlotRenderContext() to accept and mock component parameter

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

devnatan and others added 8 commits November 27, 2025 11:47
…mework/component/ItemComponent.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com>
…mework/component/ItemComponent.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com>
…mework/component/ItemComponent.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com>
…mework/component/ItemComponent.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com>
…mework/component/ItemComponent.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com>
@devnatan devnatan changed the title Explicit component keys Make component keys configurable to optimize dynamic rendering Nov 27, 2025
@devnatan devnatan merged commit 16da182 into main Nov 27, 2025
6 checks passed
@devnatan devnatan deleted the feat/component-keys branch November 27, 2025 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants