Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature support promised custom node render #594

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

hqm19
Copy link

@hqm19 hqm19 commented Apr 3, 2024

支持 custom_node_render 返回一个 promise,在 promise 的 then 中再执行所有后续动作。
当 custom_node_render 的实现,是用 ReactDOM.render 等异步方式渲染的时候,若直接执行后续动作,因这时异步渲染还没有完成,会报很多错误。有了这个支持之后, 即可以通过 custom_node_render 接入 react 等异步组件库及其庞大生态了。

有几个说明:

  1. 因为 DocumentFragment 的 render_node + parent.appendChild 的实现。 第一次 React 渲染组件时没有问题。第二次 React 渲染同样的 dom 时(update_node触发)会报错拒绝渲染:

Warning: render(...): It looks like the React-rendered content of this container was removed without using React. This is not supported and will cause errors. Instead, call ReactDOM.unmountComponentAtNode to empty a container.

因为 react 认为组件容器的内容被改过了。为了规避这个问题,在第二次异步渲染时,新建了一个 dom 节点替换原来的节点。

  1. 将 jsmind 的一个优化的地方,就是编辑提交后, topic 没有修改时的短路逻辑删除了,和 topic 有修改时一样的处理。否则在 React 的场景总是会报错。

测试用例在单独的 react + jsmind 工程中: https://github.com/hqm19/jsmind-react

Copy link
Owner

@hizzgdev hizzgdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢你的支持,大致浏览了代码并提了一些问题,建议在更多的使用场景里测试测试。
这几天我有时间也会再详细看看并进行一些测试,谢谢!

}
add_node(node) {
//logger.info('[view.add_node] node:', node);
this.create_node_element(node, this.e_nodes);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里不需要处理 Promise 的情况吗?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

逻辑上应该处理,漏掉了。但是实测上,界面新增node时貌似没有走到这里,不知道什么时候会触发。我把这里逻辑补全下

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经增加处理且测试通过。 之前 add_node 没做异步处理也没报错,是因为节点新建后立即进入了编辑,编辑完成就走update逻辑了。且新节点也没有展开按钮,所以不会有什么问题。

}
d.setAttribute('nodeid', node.id);
d.style.visibility = 'hidden';
this._reset_node_custom_style(d, node.data);

parent_node.appendChild(d);
view_data.element = d;
if (!!render_promise) {
return render_promise;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里如果永远返回一个 Promise, 后面的逻辑会不会简单一些?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为了一致性和代码简洁,是可以构造一个空 promise 返回,所有地方统一按promise处理

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修改,请看最新的代码,从 render_node 开始改为了只返回 promise, 代码简洁了很多。

element.style = origin_style;
//logger.info('[view.update_node] node:', node);

//const obj = this.render_node(element, node);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可能需要再测试一下拖动节点到其它 parent 的情况

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

哦,拖动我没用到,还没测过。回头补上

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我在 https://github.com/hqm19/jsmind-react 中,额外引入 jsmind.draggable-node.js 后:

import jsMind from "jsmind"
import "jsmind/style/jsmind.css"
import "jsmind/es6/jsmind.draggable-node.js"

原来可以work的功能就报错了:

Compiled with problems:×
ERROR in ./src/jsmind-test.jsx 9:0-45
Module not found: Error: Package path ./es6/jsmind.draggable-node.js is not exported from package /Users/xlxk/12_code/jzbj/jsmind-react/node_modules/jsmind (see exports field in /Users/xlxk/12_code/jzbj/jsmind-react/node_modules/jsmind/package.json)
``

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/hizzgdev/jsmind/blob/master/docs/zh/plugin-screenshot.md

试试这样

<script>
    import domtoimage from 'dom-to-image';
    import jsMind from 'jsmind'
    import 'jsmind/screenshot'
    import 'jsmind/style/jsmind.css'

    // ...

    var jm = new jsMind(options);
    jm.show(mind_data);
    // export current mindmap to an image
    jm.shoot()
</script>

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

拖动节点会有bug, set_node_font_style 也会有bug。

// const obj = this.render_node(element, node);
// 所以新建一个节点来渲染,并替换掉原来的节点
let d = element;
if (view_data.promise_rendered) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么只在 update_node 时才需要做这个判断? add 的时候需要吗?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

第一次渲染的时候不会有问题。同一个 容器 第二次渲染的时候才会报错。与这个问题很像: facebook/react#17547 可能与 DocumentFragment 的使用有关。所以只需在 update 时规避这个问题即可

Comment on lines 305 to 309
view_data.element = d;
element = d;
d.setAttribute('nodeid', node.id);
d.style.visibility = 'hidden';
this._reset_node_custom_style(d, node.data);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这一段是copy的 create_node_element 的逻辑吧,可以考虑把 create_node_element 也重构一下,避免相同的代码写在不同的地方。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是的,从 create_node_element 拷贝了3行。这里不需要处理 d_e 展开图标的逻辑。 d_e是不变的

// this._reset_node_custom_style(d, node.data);
// });
// }
// 上面代码,总是在编辑不改变内容退出时,节点消失。所以暂时和下面代码保持一致
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里如果直接调用 this.render_node(element, node); 会有问题吗?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

会有问题,就像注释说的,如果只调用 this.render_node(element, node); 编辑完不改内容再blur,节点就消失了。父节点收起展开后又会出来。这个原因我还没找到。所以先注释掉了。

@hqm19
Copy link
Author

hqm19 commented Apr 4, 2024

除了拖拽场景在react下还没调通,报错如上。其他的都已经按建议修改好提交了

Copy link
Owner

@hizzgdev hizzgdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢 @hqm19 , 过两天会仔细看看并亲自验证一下,谢谢!

evt: 'add_node',
data: [the_parent_node.id, node_id, topic, data, dir],
node: node_id,
return this.view.add_node(node).then(() => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里按说就会返回一个 Promise 对象了吧。那么 API 里的 add_node 这个方法的返回值也就是一个 Promise 了,我再考虑考虑,看这样搞是不是动静有点儿大,相当于不能向后兼容了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants