metal.js icon indicating copy to clipboard operation
metal.js copied to clipboard

Unexpected behavior on `defaultFn` with `replaceNonInternal`

Open yuchi opened this issue 7 years ago • 1 comments

When replaceNonInternal is called on components that have non internal properties with computed defaults, those properties are re-computed.

Test case

Given this component:

// MyButton.es.js
import templates from './MyButton.soy';
export default class MyButton extends Component {
  static STATE = {
    label: {
      value: ''
    },
    guid: {
      valueFn: () => _.uniqueId('button')
    }
  }
}
Soy.register(MyButton, templates);
// MyButton.soy
{namespace MyButton}
/**
 * @param guid
 * @param label
 */
{template .render}
  <button>{$guid} — {$label}</button>
{/template}

Used as such:

// MyApp.soy
{foreach $name in $names}
  {call MyButton.render}
    {param label: $name /}
  {/call}
{/foreach}

Now if MyApp re-renders (you add a name in $names) you’ll see the problem in action.

Expected result

Every button keeps the guid it generated.

Actual result

Every re-render computes a new guid for each button component instance.

yuchi avatar Jun 21 '17 12:06 yuchi

A very pratical solution would be to memoize defaultFn functions per-instance at creation. Potentially inefficient.

yuchi avatar Jun 21 '17 12:06 yuchi