Vaadin Flow 14刷新时节点跨状态树移动错误求助
Vaadin 14组件跨状态树迁移错误分析与解决
错误现象
清空布局后重新添加按钮时,多次刷新页面会触发以下错误:
Can't move a node from one state tree to another. If this is intentional, first remove the node from its current state tree by calling removeFromTree
错误触发代码行:
actionsLay.add(testButton);
复现该问题的简易代码(刷新页面时交替添加/移除按钮):
@Route(value = "test") @UIScope @PreserveOnRefresh public class testPanel extends Div { private static final long serialVersionUID = 5081824598131333345L; Button testButton; private HorizontalLayout actionsLay; private boolean addBtn = true; public testPanel() { testButton = new Button(new Icon(VaadinIcon.REFRESH)); actionsLay = new HorizontalLayout(); add(actionsLay); } @Override protected void onAttach(AttachEvent attachEvent) { if (addBtn) { actionsLay.removeAll(); actionsLay.add(testButton); addBtn = false; } else { actionsLay.removeAll(); addBtn = true; } } }
错误原因
问题根源在于Vaadin的状态树(StateTree)绑定机制:
- 由于使用了
@UIScope和@PreserveOnRefresh,testPanel实例会在页面刷新后被保留,不会重新创建; - 页面刷新时,Vaadin会生成新的UI状态树,但
testButton组件实例仍绑定在旧的状态树上; - 当执行
actionsLay.add(testButton)时,Vaadin尝试将组件节点转移到新状态树,但StateNode类的doSetTree方法中这段代码判断旧状态树仍处于附着(attached)状态:
isOwnerAttached = ((StateTree) getOwner()).getRootNode() .isAttached();
此时Vaadin不允许节点跨状态树迁移,因此抛出错误。
解决方案
在重新添加组件前,主动让它脱离旧状态树即可解决:
testButton.getElement().removeFromTree(); actionsLay.add(testButton);
何时需要调用removeFromTree
满足以下场景时必须调用该方法:
- 组件实例被跨UI状态树复用(比如用
@PreserveOnRefresh保留实例,刷新后UI状态树重建); - 组件需要从一个已附着的状态树转移到另一个状态树;
- 组件被移除后,后续要重新添加到不同的UI上下文(状态树)中。
内容的提问来源于stack exchange,提问作者Miłosz Bąbliński
相关产品推荐
相关产品推荐

