1.处理ref
1.创建该节点fiber对象的时候处理ref
2.stringRef才需要去挂载ref到refs上 函数ref只需要执行函数即可 对象ref直接改变current即可
流程:拿到ref 对比前后ref是否变化 没有变化则直接返回之前的ref函数 变化啦或者第一次则创建ref函数
这个函数传入value即实例(class就是this,dom就是dom元素)挂载到refs上
function coerceRef(
returnFiber: Fiber,
current: Fiber | null,
element: ReactElement,
) {
let mixedRef = element.ref;//!element就是该reactelement mixedRef就是传进来的ref
if (
mixedRef !== null &&
typeof mixedRef !== 'function' &&//!函数ref
typeof mixedRef !== 'object'//!obj的ref
) {
//!string的ref的处理 因为string是没法挂载的 函数ref和obj的ref是不需要处理的 因为直接调用函数或者设置current即可
if (__DEV__) {
if (returnFiber.mode & StrictMode) {
const componentName = getComponentName(returnFiber.type) || 'Component';
if (!didWarnAboutStringRefInStrictMode[componentName]) {
warningWithoutStack(
false,
'A string ref, "%s", has been found within a strict mode tree. ' +
'String refs are a source of potential bugs and should be avoided. ' +
'We recommend using createRef() instead.' +
'\n%s' +
'\n\nLearn more about using refs safely here:' +
'\nhttps://fb.me/react-strict-mode-string-ref',
mixedRef,
getStackByFiberInDevAndProd(returnFiber),
);
didWarnAboutStringRefInStrictMode[componentName] = true;
}
}
}
if (element._owner) {//!_owner就是当前的fiber
const owner: ?Fiber = (element._owner: any);
let inst;
if (owner) {
const ownerFiber = ((owner: any): Fiber);
invariant(
ownerFiber.tag === ClassComponent,
'Function components cannot have refs.',
);
inst = ownerFiber.stateNode;//!拿到节点的实例 dom就是dom对象 类组件就是this
}
invariant(
inst,
'Missing owner for string ref %s. This error is likely caused by a ' +
'bug in React. Please file an issue.',
mixedRef,
);
const stringRef = '' + mixedRef;
// Check if previous string ref matches new string ref
if (
current !== null &&
current.ref !== null &&
typeof current.ref === 'function' &&
current.ref._stringRef === stringRef//!每次设置完ref时候 我们都会设置ref的._stringRef为我们的stringRef 用来更新组件的时候判断stringRef是否有变化 如果没变化 我们就不需要创建一个新的ref方法了 直接返回之前的ref方法
) {
return current.ref;
}
//!生成新的ref方法
const ref = function(value) {//!value就是传入该节点的实例 这个是当这个dom/class挂载的时候会调用这个ref方法传入自己的实例
let refs = inst.refs;//!拿到rhis.refs这个对象
if (refs === emptyRefsObject) {//!如果是空
// This is a lazy pooled frozen object, so we need to initialize.
refs = inst.refs = {};
}
if (value === null) {//!如果没有节点实例
delete refs[stringRef];//!删除这个ref
} else {
refs[stringRef] = value;//!把这个ref设置到refs上
}
};
ref._stringRef = stringRef;//!设置_stringRef方便比对前后的ref
return ref;
} else {
invariant(
typeof mixedRef === 'string',
'Expected ref to be a function, a string, an object returned by React.createRef(), or null.',
);
invariant(
element._owner,
'Element ref was specified as a string (%s) but no owner was set. This could happen for one of' +
' the following reasons:\n' +
'1. You may be adding a ref to a function component\n' +
"2. You may be adding a ref to a component that was not created inside a component's render method\n" +
'3. You have multiple copies of React loaded\n' +
'See https://fb.me/react-refs-must-have-owner for more information.',
mixedRef,
);
}
}
return mixedRef;
}
2.调用ref
在commitRoot时候真正把instance挂载上去
首先调用清空ref
然后再commitLifeCycles时候(因为此时dom已经挂载啦才有实例) 拿到ref函数 如果ref是函数则直接执行这个函数传入当前instance,因为stringRef的此时ref属性也在处理ref的时候转成啦函数,所以可以和函数ref一样直接调用函数即可,然后ref是对象的时候,直接调用ref.current=instance即可
function commitAttachRef(finishedWork: Fiber) {
const ref = finishedWork.ref;//!新的ref
if (ref !== null) {
const instance = finishedWork.stateNode;
let instanceToUse;
switch (finishedWork.tag) {
case HostComponent:
instanceToUse = getPublicInstance(instance);
break;
default:
instanceToUse = instance;
}
if (typeof ref === 'function') {
ref(instanceToUse);//!调用创建时候的ref函数 传入当前instance
} else {
if (__DEV__) {
if (!ref.hasOwnProperty('current')) {
warningWithoutStack(
false,
'Unexpected ref object provided for %s. ' +
'Use either a ref-setter function or React.createRef().%s',
getComponentName(finishedWork.type),
getStackByFiberInDevAndProd(finishedWork),
);
}
}
ref.current = instanceToUse;//!对象ref直接设置current
}
}
}
总结:stringref需要在处理ref阶段创建一个ref函数转成函数ref形式 同时把ref挂载到refs对象上