问题
问:如下代码输出结果,解释原因。
export default class Index extends React.Component{
state={ num:0 }
node = null
render(){
return <div >
<div ref={(node)=>{
this.node = node
console.log('此时的参数是什么:', this.node )
}} >ref元素节点</div>
<button onClick={()=> this.setState({ num: this.state.num + 1 }) } >点击</button>
</div>
}
}
// 输出结果:
// 初始化时执行一次,输出:此时的参数是什么:<div>ref元素节点</div>
// 点击按钮时,输出两个结果:此时的参数是什么:null 和 此时的参数是什么:<div>ref元素节点</div>
// 修改为,如下代码:
export default class Index extends React.Component {
state = { num: 0 };
node = null;
getDom = (node) => {
this.node = node;
console.log("此时的参数是什么:", this.node);
};
render() {
return (
<div>
<div ref={this.getDom}>ref元素节点</div>
<button onClick={() => this.setState({ num: this.state.num + 1 })}>
点击
</button>
</div>
);
}
}
// 输出结果:
// 初始化时执行一次,输出:此时的参数是什么:<div>ref元素节点</div>
// 点击按钮不会执行,原因:ref 执行相同的函数,不会打上 Ref tag ,不会更新 ref 逻辑,表现就是 getDom 不会执行。
原因:每次更新时,给 ref 赋了新的函数,markRef 执行时,判断 current.ref !== ref
(右侧 ref 是:workInProgress.ref),就会给 fiber 打上 Ref tag ,在 commit 阶段,就会更新 ref,执行 ref 回调函数。
继续提问:为什么先要置空 ref ???
答:防止内存泄漏。
先置空 ref ,避免在一次更新中,fiber 节点卸载了,但 ref 引用没有卸载,指向了原来的元素或组件。
先说结论:防止内存泄漏
如果 ref 每次绑定一个全新的 对象(Ref.current,callback)上,而不清理对旧的 dom节点 或者 类实例 的引用,则可能会产生内存泄漏。
参考:facebook
<div ref={node => this.node1 = node}>node1 保存 div DOM 节点引用</div>
<div ref={node => this.node2 = node}>node2 保存 div DOM 节点引用</div>
{
false && <div ref={node => this.node2 = node}>
此出 div DOM 节点已卸载,react 内部调用 safeDeletionRef 后,this.node2 为 null,但是 this.node1 依旧保存着 div DOM 节点的引用。
</div>
}
TODO:这个问题怎么处理字符串的,还是没有理解
问: ref="node"
字符串,为什么会按照函数方式来处理?
答:若ref
属性是字符串时,React 会自动绑定一个函数来处理ref
逻辑。
react-reconciler/src/ReactChildFiber.js
const ref = function(value) {
let refs = inst.refs;
if (refs === emptyRefsObject) {
refs = inst.refs = {};
}
if (value === null) {
delete refs[stringRef];
} else {
refs[stringRef] = value;
}
};
ref="node"
最终会被绑定在组件实例的refs
属性上。
举例:<div ref="node"></div>
ref 函数,在 commitAttachRef 中处理如下:ref(node) // 等同于 inst.refs.node = <div>
。
问:被ref 标记的 fiber,在fiber每次更新时都会调用commitDetachRef
、commitAttachRef
吗?
答:不是。只有 ref 更新时,才会调用调用如上方法更新 ref,原始就在如上两个方法的执行时期。
问:什么是 effectTag????