React-Flow自定义节点保存额外数据时遇只读属性错误
React-Flow自定义节点保存额外数据时"attribute is read-only"错误解决
错误原因
React Flow传递给自定义节点的data对象是受控状态的一部分,内部被设置为不可直接修改(可能被冻结),直接赋值data.attribute = formData会触发只读错误,同时这种修改也不会触发组件重新渲染。
解决方案
通过更新整个elements数组来修改节点数据,遵循React状态更新的受控原则:
1. 修改自定义节点组件
添加id和onUpdateNode属性,将数据更新逻辑委托给父组件:
import { useState } from 'react'; import { Handle, Position } from 'react-flow-renderer'; import { Modal } from "react-bootstrap"; import { Form } from 'react-bootstrap'; import { triggerLimitationType } from "../../../enums/triggerLimitationType"; import _ from 'lodash'; import i18next from "../../../i18n"; function CustomNode({ data, id, onUpdateNode }) { const [show, setShow] = useState(false); const handleClose = () => setShow(false); const handleShow = () => setShow(true); // 从节点现有data中初始化表单数据,保证数据同步 const [formData, setFormData] = useState(data.attribute || { type: 0 }); const handleChange = event => { const { name, value } = event.target; const newFormData = { ...formData, [name]: value }; setFormData(newFormData); // 通知父组件更新节点数据 onUpdateNode(id, { attribute: newFormData }); } return ( <div> <div onDoubleClick={handleShow}> <Handle type="target" position={Position.Top} /> <div className="node-icon" > <i className="feather icon-skip-back"></i> </div> <Handle type="source" position={Position.Bottom} id="b" /> </div> {show && ( <Modal show={show} onHide={handleClose} className="right fade"> <Modal.Header closeButton> <Modal.Title>{i18next.t("Trigger")} : {i18next.t("VisitorReturn")}</Modal.Title> </Modal.Header> <Modal.Body> <Form.Group> <Form.Label>Type</Form.Label> <Form.Control required as="select" value={formData.type} name="type" onChange={handleChange}> {_.map(triggerLimitationType, (value, key) => ( <option value={value} key={value}>{key}</option> ))} </Form.Control> <Form.Control.Feedback type="invalid">Please choose a type.</Form.Control.Feedback> </Form.Group> </Modal.Body> </Modal> )} </div> ); } export default CustomNode;
2. 父组件实现节点更新逻辑
定义onUpdateNode函数,通过更新elements数组修改对应节点的data:
// 父组件中定义节点更新方法 const onUpdateNode = (nodeId, updates) => { // 函数式更新确保拿到最新的elements状态 setElements(prevElements => prevElements.map(node => { if (node.id === nodeId) { // 合并原有数据与更新内容 return { ...node, data: { ...node.data, ...updates } }; } return node; }) ); // 同步redux状态(如果使用redux) dispatch(updateNodeData(nodeId, updates)); }; // ReactFlow组件中配置自定义节点类型,传递onUpdateNode <ReactFlow elements={elements} nodeTypes={{ // 替换成你的自定义节点type VisitorReturn: props => <CustomNode {...props} onUpdateNode={onUpdateNode} /> }} // 其他属性... />
3. 修正节点创建逻辑
使用函数式更新获取最新的elements状态,避免旧值问题:
const onDrop = (event) => { event.preventDefault(); const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect(); const type = event.dataTransfer.getData("type"); const position = reactFlowInstance.project({ x: event.clientX - reactFlowBounds.left, y: event.clientY - reactFlowBounds.top }); const newNode = { id: getId(), position, type: type, data: { label: `${type} node`, attribute: { type: 0 } } }; // 函数式更新elements setElements(prevElements => [...prevElements, newNode]); dispatch(updateElements(newNode)); };
内容的提问来源于stack exchange,提问作者ar.gorgin
相关产品推荐
相关产品推荐

