{"record":{"id":"792f3303293fe219","repo":"HelloZeroNet/ZeroNet","slug":"functions-may-not-be-updated-on-subsequent-renders-792f33","errorCode":null,"errorMessage":"Functions may not be updated on subsequent renders (property: ${propName}). Hint: declare event handler functions outside the render() function.","messagePattern":"Functions may not be updated on subsequent renders \\(property: (.+?)\\)\\. Hint: declare event handler functions outside the render\\(\\) function\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/UiConfig/media/js/lib/maquette.js","lineNumber":244,"sourceCode":"                }\n            } else {\n                if (!propValue && typeof previousValue === 'string') {\n                    propValue = '';\n                }\n                if (propName === 'value') {\n                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {\n                        domNode[propName] = propValue;\n                        // Reset the value, even if the virtual DOM did not change\n                        domNode['oninput-value'] = undefined;\n                    }\n                    // else do not update the domNode, otherwise the cursor position would be changed\n                    if (propValue !== previousValue) {\n                        propertiesUpdated = true;\n                    }\n                } else if (propValue !== previousValue) {\n                    var type = typeof propValue;\n                    if (type === 'function') {\n                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');\n                    }\n                    if (type === 'string' && propName !== 'innerHTML') {\n                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {\n                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);\n                        } else {\n                            domNode.setAttribute(propName, propValue);\n                        }\n                    } else {\n                        if (domNode[propName] !== propValue) {\n                            domNode[propName] = propValue;\n                        }\n                    }\n                    propertiesUpdated = true;\n                }\n            }\n        }\n        return propertiesUpdated;\n    };","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/HelloZeroNet/ZeroNet/blob/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/UiConfig/media/js/lib/maquette.js#L226-L262","documentation":"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.","triggerScenarios":"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.","commonSituations":"Arrow functions defined in render(), handlers created with .bind() inside render, or closures capturing loop variables defined per-render.","solutions":["Move the handler function outside the render function so its reference is stable.","Use a memoized handler (create once in constructor/module scope and reuse).","Bind once outside render, or pass data via dataset/arguments rather than rebinding."],"exampleFix":"// before\nfunction render(item) { return h('button', { onclick: () => select(item) }, item.name); }\n// after\nvar onSelect = function (evt) { select(evt.currentTarget.dataset.item); };\nfunction render(item) { return h('button', { 'data-item': item.id, onclick: onSelect }, item.name); }","handlingStrategy":"type-guard","validationCode":"function handlersAreStable(prevProps, nextProps) {\n  return Object.keys(nextProps || {}).every(function (k) {\n    return typeof nextProps[k] !== 'function' || prevProps == null || prevProps[k] === nextProps[k];\n  });\n}\n","typeGuard":"function hasStableHandlers(prev, next) {\n  return Object.keys(next).every(function (k) { return typeof next[k] === 'function' ? (prev ? prev[k] === next[k] : false) : true; });\n}","tryCatchPattern":"try {\n  projection.update(updatedVNode);\n} catch (e) {\n  if (/Functions may not be updated on subsequent renders/.test(e.message)) {\n    console.error('Inline event handler detected; hoist it outside render():', e.message);\n  }\n  throw e;\n}","preventionTips":["Define event handlers outside render() and reuse the same reference.","Avoid .bind() and inline arrow functions in vnode properties.","Pass per-item data via data-* attributes instead of per-render closures."],"tags":["virtual-dom","event-handlers","referential-equality"],"backgroundTag":"function-property-changed-on-rerender","analyzedSha":"454c0b2e7e000fda7000cba49027541fbf327b96","analyzedAt":"2026-09-02T19:46:57.278Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}