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
Function-valued properties (typically event handlers) cannot change between renders; maquette treats functions as identity-immutable so it can skip deep-comparing them. This usually indicates a handler is recreated inside the render/renderMaquetteFunction on every render, which maquette considers a bug.
Source
Thrown at plugins/UiConfig/media/js/all.js:433
}
} 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
- Declare the handler once outside the render function (module scope, constructor, or memoized) and reference it.
- If the handler needs per-render data, capture state via closure over stable objects, or store mutable data on the domNode and read it in a stable handler.
- Use maquette's projectors' eventHandlerInterceptor pattern or move dynamic data into vnode properties the handler can access (e.g. use data-* properties).
- Wrap the changing function in a stable dispatcher created once that looks up the current behavior.
Example fix
// before
var render = function () { return h('button', { onclick: function () { counter++; } }, ['inc']); };
// after
var increment = function () { counter++; };
var render = function () { return h('button', { onclick: increment }, ['inc']); }; Defensive patterns
Strategy: type-guard
Validate before calling
function assertFunctionsStable(prevVnode, nextVnode) {
// naive structural check across props
['onclick','oninput','onchange','onkeydown'].forEach(function (ev) {
if (prevVnode && nextVnode && prevVnode.properties && nextVnode.properties &&
prevVnode.properties[ev] !== nextVnode.properties[ev]) {
console.warn('Handler ' + ev + ' changed identity between renders');
}
});
} Type guard
function isStableHandler(prev, next) {
return typeof prev !== 'function' || prev === next;
} Try / catch
try {
projection.update(vnode);
} catch (e) {
if (e.message.indexOf('Functions may not be updated') !== -1) {
console.error('Move handler outside render(); property: ' + e.message);
}
throw e;
} Prevention
- Define all event handlers once, outside the render function (module scope, class fields, closures created in constructors).
- Never use inline arrow functions or .bind() inside render for handler properties.
- For parameterized handlers, use a stable dispatcher that reads current state from a closure or the event target.
- Enable the projector's eventHandlerInterceptor in development to catch handler churn early.
When it happens
Trigger: During projection.update or a projector render pass, updateProperties encounters a function property (e.g. onclick) whose reference differs from the previous render — because the handler was defined inline in the render function.
Common situations: Inline arrow functions in JSX/render: h('button', { onclick: () => doThing(id) }); handlers built with .bind() inside render; moving from React (which accepts new handlers each render) to maquette.
Related errors
- "class" property may not be updated. Use the "classes" prope
- Provide a transitions object to the projectionOptions to do
- Style values must be strings
- Property "className" is not supported, use "class".
- ${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector
AI-assisted analysis of HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02).
Data as JSON: /api/errors/03035eaf56e34be6.
Report an issue: GitHub.