Skip to content

Commit 742d3ac

Browse files
committed
test: supplement immutability spec with React Compiler fixtures
1 parent db54efd commit 742d3ac

1 file changed

Lines changed: 111 additions & 0 deletions

File tree

plugins/eslint-plugin-react-x/src/rules/immutability/immutability.spec.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,82 @@ ruleTester.run(RULE_NAME, rule, {
589589
{ data: { name: "alias" }, messageId: "default" },
590590
],
591591
},
592+
// Mutating a value returned from useState inside a JSX event handler
593+
// (ported from React Compiler's error.invalid-function-expression-mutates-immutable-value).
594+
{
595+
code: tsx`
596+
function Component(props) {
597+
const [x, setX] = useState({ value: "" });
598+
const onChange = (e) => {
599+
x.value = e.target.value;
600+
setX(x);
601+
};
602+
return <input value={x.value} onChange={onChange} />;
603+
}
604+
`,
605+
errors: [
606+
{ data: { name: "x" }, messageId: "mutates" },
607+
{ data: { name: "x" }, messageId: "default" },
608+
],
609+
},
610+
// A callback that reassigns its own binding (ported from React Compiler's
611+
// error.function-expression-references-variable-its-assigned-to).
612+
{
613+
code: tsx`
614+
function Component() {
615+
let callback = () => {
616+
callback = null;
617+
};
618+
return <div onClick={callback} />;
619+
}
620+
`,
621+
errors: [
622+
{ data: { name: "callback" }, messageId: "mutates" },
623+
{ data: { name: "callback" }, messageId: "default" },
624+
],
625+
},
626+
// A hook that returns a function capturing and reassigning a local variable
627+
// (ported from React Compiler's error.invalid-reassign-local-in-hook-return-value).
628+
{
629+
code: tsx`
630+
function useFoo() {
631+
let x = 0;
632+
return (value) => {
633+
x = value;
634+
};
635+
}
636+
`,
637+
errors: [
638+
{ data: { name: "x" }, messageId: "default" },
639+
{ data: { name: "x" }, messageId: "mutates" },
640+
],
641+
},
642+
// Conditional reassignment plus a mutating method in a JSX event handler
643+
// (ported from React Compiler's error.mutable-range-shared-inner-outer-function).
644+
{
645+
code: tsx`
646+
function Component(props) {
647+
let a;
648+
let b;
649+
const f = () => {
650+
if (cond) {
651+
a = {};
652+
b = [];
653+
} else {
654+
a = {};
655+
b = [];
656+
}
657+
a.property = true;
658+
b.push(false);
659+
};
660+
return <div onClick={f} />;
661+
}
662+
`,
663+
errors: [
664+
{ data: { name: "a" }, messageId: "mutates" },
665+
{ data: { name: "a" }, messageId: "default" },
666+
],
667+
},
592668
],
593669
valid: [
594670
tsx`
@@ -901,5 +977,40 @@ ruleTester.run(RULE_NAME, rule, {
901977
return <Foo fn={fn} />;
902978
}
903979
`,
980+
// Reassignment to implicit globals in a JSX event handler is ignored
981+
// (ported from React Compiler's allow-reassignment-to-global-function-jsx-prop).
982+
tsx`
983+
function Component() {
984+
const onClick = () => {
985+
someUnknownGlobal = true;
986+
moduleLocal = true;
987+
};
988+
return <div onClick={onClick} />;
989+
}
990+
`,
991+
// Function-call mutations are not tracked syntactically
992+
// (ported from React Compiler's maybe-mutate-object-in-callback).
993+
tsx`
994+
function Component(props) {
995+
const object = {};
996+
const onClick = () => {
997+
mutate(object);
998+
};
999+
return <Foo callback={onClick}>{props.children}</Foo>;
1000+
}
1001+
`,
1002+
// useCallback wrapping a ref mutation is allowed
1003+
// (ported from React Compiler's useCallback-set-ref-nested-property).
1004+
tsx`
1005+
import { useCallback, useRef } from "react";
1006+
1007+
function Component() {
1008+
const ref = useRef({ inner: null });
1009+
const onChange = useCallback((event) => {
1010+
ref.current.inner = event.target.value;
1011+
});
1012+
return <input onChange={onChange} />;
1013+
}
1014+
`,
9041015
],
9051016
});

0 commit comments

Comments
 (0)