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; + } }} />); } }