Marko: After redraw input component is regenerated with incorrect key.

Created on 13 Dec 2017  路  3Comments  路  Source: marko-js/marko

Bug Report

After redraw marko regenarates key for component.

Marko Version: 4.5.6

Details

I have a modal component, that can be shown and hidden.

<div class=['modal large-modal', !state.visible && 'hidden' ]>
    <div class="modal-background" onClick("handleMaskClick") />
    <div class="exit-button" />
    <div class="modal-container">
        <div class="modal-body" key="body">
            <include (input.renderBody) />
        </div>
        <div class="modal-footer">
            <include (input.actions) />
        </div>
    </div>
</div>

I am using this component to display a timer component

<large-modal key="popup-window" on-hide('pauseTimer', 'timer')>
    <@renderBody> 
        <timer key="timer" />
    </@renderBody>
</large-modal>

This timer component has a method pause, that would pause the timer after modal is closed. To do that, i have added method on hide event on modal, that looks like this.

pauseTimer(timerKey) {
    this.getComponent(timerKey).pause();
}

This functionality does not work, as after each manipulation on modal, that is state.visibility changes, the contents are redrawn, including timer key="timer" and marko sets this components id now to timer_1, which makes my functionality with call pauseTimer('timer') to not work, as this component no longer is identified by this key.

Expected Behavior

I am expecting key to not change in scenario when component is redrawn.

needs more info todo unverified bug

Most helpful comment

@DylanPiercey We actually hit this same problem today in a drop-down component we built where the body of the dropdown is a transcluded component. If the conditional is on the include inside another component, the key gets incremented the second time the component is rendered, but the parent context still tries to lookup the key using the original key name with no index (because the parent is not aware that the component was destroyed and recreated). This seems to happen because when the component is rerendered, it uses the parent's keySequence and the ___lookup still contains the key, despite the child being destroyed. This makes sense since the destruction of the transcluded component happened in a child component so the parent component is not updated and the keySequence is not "rebuilt".

Here's a simple project that shows the issue. https://github.com/jasonmacdonald/marko-conditional-include (I'll see if I can find some time to turn this into a failing, but the demo project was what I used to narrow down the issue in our project).

There appear to be a couple options to resolve this issue:

  1. When a node for a transcluded component is detached, we could remove the key from the keySequence.__lookup in the parent context.
  2. When a component is destroyed that belongs to a parent context, we force an update on the parent even if its state didn't change, which forces the keySequence to be rebuilt (I believe) without the transcluded component. _this is our workaround for the moment_
  3. We make getComponent a bit smarter to account for keySequences rather than just using the componentLookup which only has the new key which has been incremented.

All 3 comments

@matisskeiris I created a branch which adds a test for this but I was unable to reproduce the issue.

Would you be able to pull down this branch and help in creating a failing test?

You can run the test added in my branch by running:

node ./test/__runner__/browser/cli.js --page preserve-transcluded-key-on-state-change --automated

@DylanPiercey We actually hit this same problem today in a drop-down component we built where the body of the dropdown is a transcluded component. If the conditional is on the include inside another component, the key gets incremented the second time the component is rendered, but the parent context still tries to lookup the key using the original key name with no index (because the parent is not aware that the component was destroyed and recreated). This seems to happen because when the component is rerendered, it uses the parent's keySequence and the ___lookup still contains the key, despite the child being destroyed. This makes sense since the destruction of the transcluded component happened in a child component so the parent component is not updated and the keySequence is not "rebuilt".

Here's a simple project that shows the issue. https://github.com/jasonmacdonald/marko-conditional-include (I'll see if I can find some time to turn this into a failing, but the demo project was what I used to narrow down the issue in our project).

There appear to be a couple options to resolve this issue:

  1. When a node for a transcluded component is detached, we could remove the key from the keySequence.__lookup in the parent context.
  2. When a component is destroyed that belongs to a parent context, we force an update on the parent even if its state didn't change, which forces the keySequence to be rebuilt (I believe) without the transcluded component. _this is our workaround for the moment_
  3. We make getComponent a bit smarter to account for keySequences rather than just using the componentLookup which only has the new key which has been incremented.

Fixed in #1086

Was this page helpful?
0 / 5 - 0 ratings