@@ -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