eslint-plugin-react icon indicating copy to clipboard operation
eslint-plugin-react copied to clipboard

False positive for react/jsx-no-leaked-render

Open Robloche opened this issue 3 years ago • 50 comments

Following code triggers react/jsx-no-leaked-render although it's perfectly valid:

return <MyReactComponent
          isDisabled={foo && bar === 0}
          onClick={handleOnClick} />;

with:

foo: boolean;
bar: number;

Robloche avatar May 20 '22 09:05 Robloche

cc @Belco90

ljharb avatar May 20 '22 13:05 ljharb

@ljharb thanks for pinging! I can take care of this, feel free to assign it to me.

Belco90 avatar May 20 '22 14:05 Belco90

@Robloche regarding the false positive reported: I think sometimes you would be interested into having this rule applied to props also, like:

<MyReactComponent
  header={someNumber && <Header />}
  isDisabled={foo && bar === 0}
/>

So I think the proper fix for this would be to make sure the right side of the && operator is not a condition.

Belco90 avatar May 20 '22 14:05 Belco90

Agreed. So what's you recommendation?

As for now, I simply rewrote my code, adding a local isDisabled boolean. And since this makes the code clearer, I guess it's a good solution.

Robloche avatar May 20 '22 14:05 Robloche

The right side should be able to be any value that’s safe to render, which includes booleans.

ljharb avatar May 20 '22 15:05 ljharb

The right side should be able to be any value that’s safe to render, which includes booleans.

That's true, but now I don't know what to do with this false positive since I can't figure the types of the operators. Any suggestions?

Belco90 avatar May 20 '22 20:05 Belco90

The RHS is an === comparison; it can’t ever be anything but a boolean. You don’t need to know any types to figure that part out.

ljharb avatar May 20 '22 23:05 ljharb

The RHS is an === comparison; it can’t ever be anything but a boolean. You don’t need to know any types to figure that part out.

Exactly, so since the RHS is something renderable, this rule should complain about it since we don't know the type for the LHS (it could be a zero, so the rule reported it as expected).

I was suggesting to report props only when there is a && operator and the RHS is a component so we can assume you want to pass that prop to render something, rather than just use the value for logic purposes.

Belco90 avatar May 21 '22 08:05 Belco90

Yes, I'm aligned with your suggestion. That should do the trick.

Robloche avatar May 23 '22 07:05 Robloche

This could be even fixed by introducing a new option: reportProps. It could receive the following values:

  • "always": as it is now
  • "components": when the RHS is a component, as suggested
  • "never": so no props are reported

Belco90 avatar May 23 '22 07:05 Belco90

@Belco90 in this example tho, foo is a boolean since it's using the TS parser - so the rule should be able to know that the entire prop is only a boolean.

ljharb avatar May 24 '22 15:05 ljharb

@Belco90 in this example tho, foo is a boolean since it's using the TS parser - so the rule should be able to know that the entire prop is only a boolean.

Is it? If so, it's ok for that case. What about the cases described before?

Belco90 avatar May 24 '22 15:05 Belco90

In the case of https://github.com/jsx-eslint/eslint-plugin-react/issues/3292#issuecomment-1132947856, with no type information, obviously both of those should warn.

ljharb avatar May 24 '22 16:05 ljharb

I'm also getting a very incorrect error for this:

const DISPLAY = {
  WELCOME: 'WELCOME',
  FOO: 'FOO'
};

const Component = () => {
  const [display, setDisplay] = useState(DISPLAY.WELCOME);

  const test = !display || display === DISPLAY.WELCOME;

  console.log(typeof(test)); // logs 'boolean'
    
  return (
    <div>
      {(!display || display === DISPLAY.WELCOME) && (
         <span>my condition produces a linting error even though it is correctly interpreted as a boolean</span>
      )}
    </div>
  );
};

texonidas avatar May 26 '22 03:05 texonidas

In that case, since !display and display === something are booleans, there shouldn't be any TS required to statically know the condition is safe (the runtime typeof is irrelevant)

ljharb avatar May 26 '22 04:05 ljharb

@ljharb I'll need way more time to try checking the TS types when available for the operators of the logical expression as discussed in this issue, since I've never done this before.

Belco90 avatar May 26 '22 23:05 Belco90

Not sure it can help but I have been using this relatively simple rule so far https://github.com/grailed-code/eslint-plugin-grailed/blob/main/lib/rules/jsx-no-ampersands.js Tests: https://github.com/grailed-code/eslint-plugin-grailed/blob/main/tests/lib/rules/jsx-no-ampersands.js It doesn't raise the false positives, even without any knowledge of the types.

fabiendem avatar May 27 '22 10:05 fabiendem

We don’t want to forbid && entirely tho - many uses of it are perfectly fine.

ljharb avatar May 27 '22 14:05 ljharb

A new option would be good. Props should not be validated by default.

Also the fix should not result to null in ternary operator but to undefined or it should be configurable. null according to Crockford should not be used: if you want a falsy value use undefined, because null does not work with default params and therefore completely changes the code flow.

pke avatar Jun 22 '22 10:06 pke

Also the fix should not result to null in ternary operator but to undefined or it should be configurable. null according to Crockford should not be used: if you want a falsy value use undefined, because null does not work with default params and therefore completely changes the code flow.

Returning null is required since prior to React v18 returning undefined is considered as a missed return statement. React itself will complain with a message like: Uncaught Error: Error(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

Belco90 avatar Jun 22 '22 11:06 Belco90

re: null thats correct for render returns but not for props. I was unclear. I meant for props fixes it should use undefined and not null.

pke avatar Jun 22 '22 12:06 pke

@pke I see, I guess we can discuss on how to improve the rule reporting on props other than children. Not really sure what to do with it to be honest, apart from the option to decide between null or undefined.

Belco90 avatar Jun 22 '22 12:06 Belco90

Given those 2 components:

function Button({ type = "primary"}) {
 ...
}

function Parent({ count: number }) {
  return <Button type={count && "secondary"}/>
}

the linting rule would change this:

-return <Button type={count && "secondary"}/>
+return <Button type={count ? "secondary": null }/>

And when it sends null for type to the <Button> the Buttons default assignment for the type prop does not work anymore.

pke avatar Jun 23 '22 05:06 pke

"null should not be used" is useless and antiquated advice that the larger JS ecosystem has resoundingly rejected, so let's just throw that out the window right now.

ljharb avatar Jun 23 '22 05:06 ljharb

You see the problem with null in that case though?

pke avatar Jun 23 '22 05:06 pke

Yes, but a present undefined prop isn't the same thing as an absent prop either.

In this specific case, if the prop value is foo && bar === 0 then the rule simply shouldn't be checking it - so yes, I do agree that this rule should not be checking prop values that aren't "children on an HTML element".

ljharb avatar Jun 23 '22 05:06 ljharb

Yes, but a present undefined prop isn't the same thing as an absent prop either.

Like for the JSX compiler? Would you mind to explain?

pke avatar Jun 23 '22 09:06 pke

Like for the props object the component receives. <Foo /> and <Foo x={undefined} /> are different - the former gets a props object of {}, the latter { x: undefined }, and these are observably different.

ljharb avatar Jun 23 '22 13:06 ljharb

Has typescript awareness landed for this rule?

shamilovtim avatar Jun 29 '22 16:06 shamilovtim

Has typescript awareness landed for this rule?

@shamilovtim I'm afraid it didn't, and I'm not really available to handle this for now.

Belco90 avatar Jun 29 '22 18:06 Belco90