Skip to content

Commit

Permalink
Add ref cleanup function
Browse files Browse the repository at this point in the history
  • Loading branch information
sammy-SC committed Nov 15, 2022
1 parent c54e354 commit d8e5b2d
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 18 deletions.
66 changes: 66 additions & 0 deletions packages/react-dom/src/__tests__/refs-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,3 +534,69 @@ describe('strings refs across renderers', () => {
expect(inst.refs.child2).toBe(div2.firstChild);
});
});

describe('refs return clean up function', () => {
it('does not break', () => {
const container = document.createElement('div');
let cleanUp = jest.fn();
let setup = jest.fn();

ReactDOM.render(
<div
ref={_ref => {
setup(_ref);
return cleanUp;
}}
/>,
container,
);

ReactDOM.render(
<div
ref={_ref => {
setup(_ref);
}}
/>,
container,
);

expect(setup).toHaveBeenCalledTimes(2);
expect(cleanUp).toHaveBeenCalledTimes(1);
expect(cleanUp.mock.calls[0][0]).toBe(undefined);

ReactDOM.render(<div ref={_ref => {}} />, container);

expect(cleanUp).toHaveBeenCalledTimes(1);
expect(setup).toHaveBeenCalledTimes(3);
expect(setup.mock.calls[2][0]).toBe(null);

cleanUp = jest.fn();
setup = jest.fn();

ReactDOM.render(
<div
ref={_ref => {
setup(_ref);
return cleanUp;
}}
/>,
container,
);

expect(setup).toHaveBeenCalledTimes(1);
expect(cleanUp).toHaveBeenCalledTimes(0);

ReactDOM.render(
<div
ref={_ref => {
setup(_ref);
return cleanUp;
}}
/>,
container,
);

expect(setup).toHaveBeenCalledTimes(2);
expect(cleanUp).toHaveBeenCalledTimes(1);
});
});
3 changes: 3 additions & 0 deletions packages/react-reconciler/src/ReactFiber.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ function FiberNode(
this.index = 0;

this.ref = null;
this.refCleanup = null;

this.pendingProps = pendingProps;
this.memoizedProps = null;
Expand Down Expand Up @@ -336,6 +337,7 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
workInProgress.refCleanup = null;

if (enableProfilerTimer) {
workInProgress.selfBaseDuration = current.selfBaseDuration;
Expand Down Expand Up @@ -880,6 +882,7 @@ export function assignFiberPropertiesInDEV(
target.sibling = source.sibling;
target.index = source.index;
target.ref = source.ref;
target.refCleanup = source.refCleanup;
target.pendingProps = source.pendingProps;
target.memoizedProps = source.memoizedProps;
target.updateQueue = source.updateQueue;
Expand Down
3 changes: 3 additions & 0 deletions packages/react-reconciler/src/ReactFiber.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ function FiberNode(
this.index = 0;

this.ref = null;
this.refCleanup = null;

this.pendingProps = pendingProps;
this.memoizedProps = null;
Expand Down Expand Up @@ -336,6 +337,7 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
workInProgress.refCleanup = null;

if (enableProfilerTimer) {
workInProgress.selfBaseDuration = current.selfBaseDuration;
Expand Down Expand Up @@ -880,6 +882,7 @@ export function assignFiberPropertiesInDEV(
target.sibling = source.sibling;
target.index = source.index;
target.ref = source.ref;
target.refCleanup = source.refCleanup;
target.pendingProps = source.pendingProps;
target.memoizedProps = source.memoizedProps;
target.updateQueue = source.updateQueue;
Expand Down
35 changes: 26 additions & 9 deletions packages/react-reconciler/src/ReactFiberCommitWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,29 @@ function safelyAttachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {

function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {
const ref = current.ref;
if (ref !== null) {
const refCleanup = current.refCleanup;

if (refCleanup !== null) {
if (typeof ref === 'function') {
try {
if (shouldProfile(current)) {
try {
startLayoutEffectTimer();
refCleanup();
} finally {
recordLayoutEffectDuration(current);
}
} else {
refCleanup();
}
} catch (error) {
captureCommitPhaseError(current, nearestMountedAncestor, error);
}
} else {
// $FlowFixMe unable to narrow type to RefObject
ref.refCleanup = null;
}
} else if (ref !== null) {
if (typeof ref === 'function') {
let retVal;
try {
Expand Down Expand Up @@ -1583,14 +1605,9 @@ function commitAttachRef(finishedWork: Fiber) {
} else {
retVal = ref(instanceToUse);
}
if (__DEV__) {
if (typeof retVal === 'function') {
console.error(
'Unexpected return value from a callback ref in %s. ' +
'A callback ref should not return a function.',
getComponentNameFromFiber(finishedWork),
);
}

if (typeof retVal === 'function') {
finishedWork.refCleanup = retVal;
}
} else {
if (__DEV__) {
Expand Down
35 changes: 26 additions & 9 deletions packages/react-reconciler/src/ReactFiberCommitWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,29 @@ function safelyAttachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {

function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {
const ref = current.ref;
if (ref !== null) {
const refCleanup = current.refCleanup;

if (refCleanup !== null) {
if (typeof ref === 'function') {
try {
if (shouldProfile(current)) {
try {
startLayoutEffectTimer();
refCleanup();
} finally {
recordLayoutEffectDuration(current);
}
} else {
refCleanup();
}
} catch (error) {
captureCommitPhaseError(current, nearestMountedAncestor, error);
}
} else {
// $FlowFixMe unable to narrow type to RefObject
ref.refCleanup = null;
}
} else if (ref !== null) {
if (typeof ref === 'function') {
let retVal;
try {
Expand Down Expand Up @@ -1583,14 +1605,9 @@ function commitAttachRef(finishedWork: Fiber) {
} else {
retVal = ref(instanceToUse);
}
if (__DEV__) {
if (typeof retVal === 'function') {
console.error(
'Unexpected return value from a callback ref in %s. ' +
'A callback ref should not return a function.',
getComponentNameFromFiber(finishedWork),
);
}

if (typeof retVal === 'function') {
finishedWork.refCleanup = retVal;
}
} else {
if (__DEV__) {
Expand Down
2 changes: 2 additions & 0 deletions packages/react-reconciler/src/ReactInternalTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ export type Fiber = {
| (((handle: mixed) => void) & {_stringRef: ?string, ...})
| RefObject,

refCleanup: null | (() => void),

// Input is the data coming into process this fiber. Arguments. Props.
pendingProps: any, // This type will be more specific once we overload the tag.
memoizedProps: any, // The props used to create the output.
Expand Down

0 comments on commit d8e5b2d

Please sign in to comment.