self.complete()
 
 
-class _UpdatingMixin(AutoAttrMixin):
-    _on_update: Callable
+class IntoUpdateValueMixin(AutoAttrMixin):
+    'Provides .into_update_value(), exportable for module-external consumers.'
 
-    def __init__(self, on_update: Callable, **kwargs) -> None:
-        super().__init__(**kwargs)
-        self._on_update = on_update
-
-    @property
-    def static_copy(self):
+    def into_update_value(self) -> Any:
         'Return non-updating copy of self.'
         if isinstance(self, _Dict):
             return None
                     else attr_val.static_copy)
         return obj
 
+
+class _UpdatingMixin(IntoUpdateValueMixin):
+    _on_update: Callable
+
+    def __init__(self, on_update: Callable, **kwargs) -> None:
+        super().__init__(**kwargs)
+        self._on_update = on_update
+
     def _make_attr(self, cls: Callable, key: str):
         return cls(on_update=lambda *steps: self._on_update(key, *steps))
 
 
 from ircplom.tui_base import (BaseTui, PromptWidget, TuiEvent, Window,
                               CMD_SHORTCUTS)
 from ircplom.client import (
-        AutoAttrMixin, Client, ClientQueueMixin, Dict, DictItem, IrcConnSetup,
-        LogScope, NewClientEvent, NickUserHost, ServerCapability,
-        SharedClientDbFields, Topic)
+        AutoAttrMixin, Client, ClientQueueMixin, Dict, DictItem,
+        IntoUpdateValueMixin, IrcConnSetup, LogScope, NewClientEvent,
+        NickUserHost, ServerCapability, SharedClientDbFields, Topic)
 
 CMD_SHORTCUTS['disconnect'] = 'window.disconnect'
 CMD_SHORTCUTS['join'] = 'window.join'
         value = ((parent[last_step] if last_step in parent.keys()
                   else None) if isinstance(parent, Dict)
                  else getattr(parent, last_step))
-        if value and hasattr(value, 'static_copy'):
-            value = value.static_copy
+        if isinstance(value, IntoUpdateValueMixin):
+            value = value.into_update_value()
         self._client_tui_trigger('update_db', update=_Update(path, value))