赞
踩
1 )概述
2 )源码
定位到 packages/react-reconciler/src/ReactChildFiber.js#L1108
function reconcileSingleElement( returnFiber: Fiber, currentFirstChild: Fiber | null, element: ReactElement, expirationTime: ExpirationTime, ): Fiber { // ... 跳过很多代码 while (child !== null) { // TODO: If key === null and child.key === null, then this only applies to // the first item in the list. if (child.key === key) { if ( child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type ) { // ... 跳过很多代码 // 注意这里 existing.ref = coerceRef(returnFiber, child, element); // ... 跳过很多代码 } else { deleteRemainingChildren(returnFiber, child); break; } } else { deleteChild(returnFiber, child); } child = child.sibling; } if (element.type === REACT_FRAGMENT_TYPE) { // ... 跳过很多代码 return created; } else { // ... 跳过很多代码 // 注意这里 created.ref = coerceRef(returnFiber, currentFirstChild, element); created.return = returnFiber; return created; } }
coerceRef
function coerceRef( returnFiber: Fiber, current: Fiber | null, element: ReactElement, ) { let mixedRef = element.ref; // 拿到 ref // function ref 和 object ref 是不需要经过特殊处理的 // 自己处理节点对象挂载到 class component 的 this 上面这一个过程 // 对于 string ref,它只是一个string,它没有任何功能,它的挂载是要react这边来帮着去做的 // 所以这边主要去 去处理一下 string ref 的一个实现过程 if ( mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object' ) { if (__DEV__) { // 跳过 } // 在 ReactElement.js 中可看到 _owner 是 ReactCurrentOwner.current // 在更新 class component 的时候,调用 finishClassComponent 就会设置 // ReactCurrentOwner.current = workInProgress // 在后面调用 instance.render() 去重新渲染子节点的过程中 // 就会调用 React.createElement, 因为 ref 只有 // 因为ref它肯定是在 class component,它这个过程当中才会被创建的 // 因为只有 class component 有 this 去挂载 ref 的那个对象 // 所以我们在调用 instance.render() 的时候,那么在 react element 里面拿到的这个 ReactCurrentOwner.current // 就是我们那个 class component,它对应的fiber对象 if (element._owner) { const owner: ?Fiber = (element._owner: any); let inst; // 有了这个 fiber 对象之后,那么我们可以拿到它的 _owner,拿到它的 _owner 之后 // 如果 owner 的存在,ownerFiber 就等于 owner if (owner) { const ownerFiber = ((owner: any): Fiber); invariant( ownerFiber.tag === ClassComponent, 'Function components cannot have refs.', ); // 然后它的 inst 就是 ownerFiber.stateNode // 也就是我们 class component,那个 instance 也就是 this inst = ownerFiber.stateNode; } 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 // 有了 inst 之后,可以为它去构建一个方法 // 这边是一个对比,就是说我们每次设置完这个 ref 之后,都会给它设置一个属性 _stringRef // 用来在我们更新这个组件的过程当中,去判断一下它这个 _stringRef 对应的那个值是否有变化 if ( current !== null && current.ref !== null && typeof current.ref === 'function' && current.ref._stringRef === stringRef ) { // 如果没有变化,我们不需要去为它重新生成一个方法了,我们只需要 return 就可以了 return current.ref; } // 对于新的情况,我们就需要去生成一个方法 // 这个 value 就是后期dom节点或者是class instance它被挂载的时候 // 它会调用这个 ref 这个方法,然后传入它自己的那个实例 // 也就是给一个 dom 节点设置了 ref 之后,在后期就是 commitRoot 的过程当中,这个节点最终被挂载到 dom 上面了 // 这个时候会把这个 dom 节点去调用 ref 这个方法,然后作为参数传入进来 // 这个时候去设置到就是当前去创建这个 ref 的时候,这个 class component的对象上面的 ref 这个属性上面 // 也就达到了 ref 可以设置在 this.refs 上面这个功能 const ref = function(value) { let refs = inst.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]; } else { refs[stringRef] = value; } }; ref._stringRef = stringRef; 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; }
关于 commit开始之前先detach
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L392
查看 commitAllHostEffects
function commitAllHostEffects() { while (nextEffect !== null) { if (__DEV__) { ReactCurrentFiber.setCurrentFiber(nextEffect); } recordEffect(); const effectTag = nextEffect.effectTag; if (effectTag & ContentReset) { commitResetTextContent(nextEffect); } // 对于有 ref 这个 SideEffect 的节点 // 如果current不等于null,要先调用 commitDetachRef // 先把这个 ref 从之前挂载的地方去给它卸载下来,看下这个 commitDetachRef if (effectTag & Ref) { const current = nextEffect.alternate; if (current !== null) { commitDetachRef(current); } } // The following switch statement is only concerned about placement, // updates, and deletions. To avoid needing to add a case for every // possible bitmap value, we remove the secondary effects from the // effect tag and switch on that value. let primaryEffectTag = effectTag & (Placement | Update | Deletion); switch (primaryEffectTag) { case Placement: { commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is inserted, before // any life-cycles like componentDidMount gets called. // TODO: findDOMNode doesn't rely on this any more but isMounted // does and isMounted is deprecated anyway so we should be able // to kill this. nextEffect.effectTag &= ~Placement; break; } case PlacementAndUpdate: { // Placement commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is inserted, before // any life-cycles like componentDidMount gets called. nextEffect.effectTag &= ~Placement; // Update const current = nextEffect.alternate; commitWork(current, nextEffect); break; } case Update: { const current = nextEffect.alternate; commitWork(current, nextEffect); break; } case Deletion: { commitDeletion(nextEffect); break; } } nextEffect = nextEffect.nextEffect; } if (__DEV__) { ReactCurrentFiber.resetCurrentFiber(); } }
定位到 packages/react-reconciler/src/ReactFiberCommitWork.js#L623
查看 commitDetachRef
function commitDetachRef(current: Fiber) {
const currentRef = current.ref;
if (currentRef !== null) {
if (typeof currentRef === 'function') {
currentRef(null);
} else {
currentRef.current = null;
}
}
}
到 commitAllLifeCycles
时
function commitAllLifeCycles(
finishedRoot: FiberRoot,
committedExpirationTime: ExpirationTime,
) {
// ... 跳过很多代码
while (nextEffect !== null) {
// ... 跳过很多代码
// 注意这里
if (effectTag & Ref) {
recordEffect();
commitAttachRef(nextEffect);
}
// ... 跳过很多代码
}
}
commitAttachRef
把真正的更新过后的节点给它挂载上去function commitAttachRef(finishedWork: Fiber) { const ref = finishedWork.ref; if (ref !== null) { const instance = finishedWork.stateNode; let instanceToUse; switch (finishedWork.tag) { case HostComponent: instanceToUse = getPublicInstance(instance); // 获取到了 dom节点对应到的实例 break; default: instanceToUse = instance; } // function 的处理 if (typeof ref === 'function') { ref(instanceToUse); } 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; } } }
HostComponent
执行 getPublicInstance
export function getPublicInstance(instance: Instance): * {
return instance;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。