diff --git a/codemods/create-element-to-jsx/scripts/error-tests.mjs b/codemods/create-element-to-jsx/scripts/error-tests.mjs
index 5480d1f..d24d518 100644
--- a/codemods/create-element-to-jsx/scripts/error-tests.mjs
+++ b/codemods/create-element-to-jsx/scripts/error-tests.mjs
@@ -43,5 +43,5 @@ test("throws on unsupported props literal", () => {
const result = runCodemod("var React = require('react/addons');\nReact.createElement('foo', 1);\n");
const combined = `${result.stdout}\n${result.stderr}`;
assert.match(combined, /Unexpected attribute of type .*Literal/);
- assert.match(combined, /Failed to execute codemod|InitializationFailed/);
+ assert.match(combined, /Failed to execute codemod|InitializationFailed|Workflow failed after|codemod::runtime::javascript/);
});
diff --git a/codemods/replace-string-ref/README.md b/codemods/replace-string-ref/README.md
index dfbb6b7..ca63af0 100644
--- a/codemods/replace-string-ref/README.md
+++ b/codemods/replace-string-ref/README.md
@@ -2,6 +2,8 @@
Replace string refs in React class components with callback refs that assign through `this.refs`.
+On unmount, React invokes callback refs with `null`. The generated callback deletes the `this.refs` property in that case (instead of assigning `null`) so behavior stays closer to string refs for checks like `typeof this.refs.refName`, `'refName' in this.refs`, and `Object.keys(this.refs)`.
+
## Usage
```bash
diff --git a/codemods/replace-string-ref/scripts/codemod.ts b/codemods/replace-string-ref/scripts/codemod.ts
index 66885df..cb5aff4 100644
--- a/codemods/replace-string-ref/scripts/codemod.ts
+++ b/codemods/replace-string-ref/scripts/codemod.ts
@@ -53,7 +53,11 @@ function callbackRefText(refName: string): string {
? `this.refs.${refName}`
: `this.refs[${JSON.stringify(refName)}]`;
return `ref={(ref) => {
- ${assignmentTarget} = ref;
+ if (ref === null) {
+ delete ${assignmentTarget};
+ } else {
+ ${assignmentTarget} = ref;
+ }
}}`;
}
diff --git a/codemods/replace-string-ref/tests/class-component-custom-import-names/expected.tsx b/codemods/replace-string-ref/tests/class-component-custom-import-names/expected.tsx
index 70193fe..83610be 100644
--- a/codemods/replace-string-ref/tests/class-component-custom-import-names/expected.tsx
+++ b/codemods/replace-string-ref/tests/class-component-custom-import-names/expected.tsx
@@ -3,7 +3,11 @@ import React1, { PureComponent as PureComponent1 } from "react";
class C extends React1.Component {
render() {
return (
{
- this.refs.refName = ref;
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
}} />);
}
}
@@ -11,7 +15,11 @@ class C extends React1.Component {
class C1 extends PureComponent1 {
render() {
return (
{
- this.refs.refName = ref;
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
}} />);
}
}
diff --git a/codemods/replace-string-ref/tests/class-component-default-import/expected.tsx b/codemods/replace-string-ref/tests/class-component-default-import/expected.tsx
index 90367dd..7867858 100644
--- a/codemods/replace-string-ref/tests/class-component-default-import/expected.tsx
+++ b/codemods/replace-string-ref/tests/class-component-default-import/expected.tsx
@@ -3,7 +3,11 @@ import React from "react";
class C extends React.Component {
render() {
return (
{
- this.refs.refName = ref;
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
}} />);
}
}
@@ -11,7 +15,11 @@ class C extends React.Component {
class C1 extends React.PureComponent {
render() {
return (
{
- this.refs.refName = ref;
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
}} />);
}
}
diff --git a/codemods/replace-string-ref/tests/class-component-named-import/expected.tsx b/codemods/replace-string-ref/tests/class-component-named-import/expected.tsx
index cac99e6..c824142 100644
--- a/codemods/replace-string-ref/tests/class-component-named-import/expected.tsx
+++ b/codemods/replace-string-ref/tests/class-component-named-import/expected.tsx
@@ -3,7 +3,11 @@ import { Component, PureComponent } from "react";
class C extends Component {
render() {
return (
{
- this.refs.refName = ref;
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
}} />);
}
}
@@ -11,7 +15,11 @@ class C extends Component {
class C1 extends PureComponent {
render() {
return (
{
- this.refs.refName = ref;
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
}} />);
}
}
diff --git a/codemods/replace-string-ref/tests/delete-ref-on-unmount/expected.tsx b/codemods/replace-string-ref/tests/delete-ref-on-unmount/expected.tsx
new file mode 100644
index 0000000..e9e65a6
--- /dev/null
+++ b/codemods/replace-string-ref/tests/delete-ref-on-unmount/expected.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+
+class C extends React.Component {
+ render() {
+ return (
{
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
+ }} />);
+ }
+}
diff --git a/codemods/replace-string-ref/tests/delete-ref-on-unmount/input.tsx b/codemods/replace-string-ref/tests/delete-ref-on-unmount/input.tsx
new file mode 100644
index 0000000..87d3ec8
--- /dev/null
+++ b/codemods/replace-string-ref/tests/delete-ref-on-unmount/input.tsx
@@ -0,0 +1,7 @@
+import React from "react";
+
+class C extends React.Component {
+ render() {
+ return
;
+ }
+}
diff --git a/codemods/replace-string-ref/tests/delete-ref-on-unmount/metrics.json b/codemods/replace-string-ref/tests/delete-ref-on-unmount/metrics.json
new file mode 100644
index 0000000..69d5881
--- /dev/null
+++ b/codemods/replace-string-ref/tests/delete-ref-on-unmount/metrics.json
@@ -0,0 +1,11 @@
+{
+ "string-ref-replacements": [
+ {
+ "cardinality": {
+ "file": "tests/delete-ref-on-unmount/input.tsx",
+ "refs": "refName"
+ },
+ "count": 1
+ }
+ ]
+}
\ No newline at end of file
diff --git a/codemods/replace-string-ref/tests/export-default-class/expected.tsx b/codemods/replace-string-ref/tests/export-default-class/expected.tsx
index 3ee5887..ef9b259 100644
--- a/codemods/replace-string-ref/tests/export-default-class/expected.tsx
+++ b/codemods/replace-string-ref/tests/export-default-class/expected.tsx
@@ -1,4 +1,8 @@
import React from "react";
export default class C extends React.Component { render() { return (
{
- this.refs.refName = ref;
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
}} />); } }
diff --git a/codemods/replace-string-ref/tests/multiple-string-refs/expected.tsx b/codemods/replace-string-ref/tests/multiple-string-refs/expected.tsx
index 68c052a..535eea5 100644
--- a/codemods/replace-string-ref/tests/multiple-string-refs/expected.tsx
+++ b/codemods/replace-string-ref/tests/multiple-string-refs/expected.tsx
@@ -1,6 +1,14 @@
import React from "react";
class C extends React.Component { render() { return <>
{
- this.refs.a = ref;
+ if (ref === null) {
+ delete this.refs.a;
+ } else {
+ this.refs.a = ref;
+ }
}} />
{
- this.refs.b = ref;
+ if (ref === null) {
+ delete this.refs.b;
+ } else {
+ this.refs.b = ref;
+ }
}} />>; } }
diff --git a/codemods/replace-string-ref/tests/namespace-import/expected.tsx b/codemods/replace-string-ref/tests/namespace-import/expected.tsx
index 69b90a7..80da5b7 100644
--- a/codemods/replace-string-ref/tests/namespace-import/expected.tsx
+++ b/codemods/replace-string-ref/tests/namespace-import/expected.tsx
@@ -1,4 +1,8 @@
import * as React1 from "react";
class C extends React1.Component { render() { return ( {
- this.refs.refName = ref;
+ if (ref === null) {
+ delete this.refs.refName;
+ } else {
+ this.refs.refName = ref;
+ }
}} />); } }
diff --git a/codemods/replace-string-ref/tests/non-identifier-ref-name/expected.tsx b/codemods/replace-string-ref/tests/non-identifier-ref-name/expected.tsx
index e31278e..902784e 100644
--- a/codemods/replace-string-ref/tests/non-identifier-ref-name/expected.tsx
+++ b/codemods/replace-string-ref/tests/non-identifier-ref-name/expected.tsx
@@ -1,4 +1,8 @@
import React from "react";
class C extends React.Component { render(){ return (
{
- this.refs["foo-bar"] = ref;
+ if (ref === null) {
+ delete this.refs["foo-bar"];
+ } else {
+ this.refs["foo-bar"] = ref;
+ }
}} />); } }