HelloZeroNet/ZeroNet · error · Error

Functions may not be updated on subsequent renders (property

Error message

Functions may not be updated on subsequent renders (property: ${propName}). Hint: declare event handler functions outside the render() function.

What it means

When diffing properties, maquette throws if a property whose new value is a function differs from the previous value. Function values cannot be compared structurally, and inline handlers are recreated every render, so maquette forces handlers to be stable references to keep diffing safe and predictable.

Source

Thrown at plugins/UiConfig/media/js/lib/maquette.js:244

                }
            } else {
                if (!propValue && typeof previousValue === 'string') {
                    propValue = '';
                }
                if (propName === 'value') {
                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {
                        domNode[propName] = propValue;
                        // Reset the value, even if the virtual DOM did not change
                        domNode['oninput-value'] = undefined;
                    }
                    // else do not update the domNode, otherwise the cursor position would be changed
                    if (propValue !== previousValue) {
                        propertiesUpdated = true;
                    }
                } else if (propValue !== previousValue) {
                    var type = typeof propValue;
                    if (type === 'function') {
                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');
                    }
                    if (type === 'string' && propName !== 'innerHTML') {
                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
                        } else {
                            domNode.setAttribute(propName, propValue);
                        }
                    } else {
                        if (domNode[propName] !== propValue) {
                            domNode[propName] = propValue;
                        }
                    }
                    propertiesUpdated = true;
                }
            }
        }
        return propertiesUpdated;
    };

View on GitHub (pinned to 454c0b2e7e)

Solutions

  1. Move the handler function outside the render function so its reference is stable.
  2. Use a memoized handler (create once in constructor/module scope and reuse).
  3. Bind once outside render, or pass data via dataset/arguments rather than rebinding.

Example fix

// before
function render(item) { return h('button', { onclick: () => select(item) }, item.name); }
// after
var onSelect = function (evt) { select(evt.currentTarget.dataset.item); };
function render(item) { return h('button', { 'data-item': item.id, onclick: onSelect }, item.name); }
Defensive patterns

Strategy: type-guard

Validate before calling

function handlersAreStable(prevProps, nextProps) {
  return Object.keys(nextProps || {}).every(function (k) {
    return typeof nextProps[k] !== 'function' || prevProps == null || prevProps[k] === nextProps[k];
  });
}

Type guard

function hasStableHandlers(prev, next) {
  return Object.keys(next).every(function (k) { return typeof next[k] === 'function' ? (prev ? prev[k] === next[k] : false) : true; });
}

Try / catch

try {
  projection.update(updatedVNode);
} catch (e) {
  if (/Functions may not be updated on subsequent renders/.test(e.message)) {
    console.error('Inline event handler detected; hoist it outside render():', e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: Defining an event handler inline inside a render function, e.g. h('button', { onclick: () => doThing(item) }), so each render creates a new function reference and updateProperties sees a changed function property.

Common situations: Arrow functions defined in render(), handlers created with .bind() inside render, or closures capturing loop variables defined per-render.

Related errors


AI-assisted analysis of HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02). Data as JSON: /api/errors/792f3303293fe219. Report an issue: GitHub.