4

My question is about conditionally rendering in JSX & the use of this.

Consider the following code in my React application:

render() {
    return (
        <li onMouseEnter={this.mouseEnter}>
          //things
        </li>
      )
    }

I would like to conditionally render the onMouseEnter attribute so that it doesn't get applied every time the component gets called and displays the <li> tag.

For example:

render() {
    return (
        <li {this.renderMouseEvents()} >
          ...
        </li>
      )
    }

But visual studio complains and says that "[js] "..." expected"

Obviously calling { this.renderMouseEvents() } outside the HTML element accepts the use of this.

Why is this not valid inside an html element in JSX?

What is a proper/cleaner way to accomplish this conditional rendering in JSX?

Any help is appreciated, thanks!

lsimonetti
  • 1,422
  • 3
  • 16
  • 26
  • 2
    Why not simply add your condition in your mouseEnter function ? – Nevosis Aug 08 '17 at 13:40
  • 3
    This question presents a [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Your JSX snippet is syntactically invalid but the `this` usage has little to do with it. – noppa Aug 08 '17 at 13:52

5 Answers5

2

Just insert your conditional into the attribute:

import noop from 'lodash/noop';

render() {
    return (
        <li onMouseEnter={ifSatisfies ? this.mouseEnter : noop}>
          //things
        </li>
      )
    }
Denialos
  • 956
  • 7
  • 16
1

What you have written is not valid JSX syntax. Try the following instead:

mouseEnterBehaviorEnabled() is a function that returns true or false. Here you put your logic for when you want mouseEnter to trigger.

Take a look at the Short circuit evaluation on MDN.

render() {
  return (
    <li onMouseEnter={this.mouseEnterBehaviorEnabled() && this.mouseEnter} >
      ...
    </li>
  );
}

You could of course also just do

render() {
  return (
    <li onMouseEnter={this.mouseEnter} >
      ...
    </li>
  );
}

and run your logic inside mouseEnter before the rest of the original behavior. If those conditions fail, just return false and the rest won't get executed.

Chris
  • 57,622
  • 19
  • 111
  • 137
  • What happens when that expression gets evaluated as false? Does the `onMouseEnter` attribute simply get omitted by react, or does it get rendered as `onMouseEnter={}`? – lsimonetti Aug 08 '17 at 13:54
  • I believe you are confusing the syntax with the traditional inline HTML event handlers. If you inspect your code there are no `onMouseEnter` "attributes" and they don't get rendered. But long story short; because `mouseEnterBehaviorEnabled()` is `false` the expression `this.mouseEnterBehaviorEnabled() && this.mouseEnter` is also false, meaning that `onMouseEnter` is false too - so, nothing will happen. – Chris Aug 08 '17 at 14:01
  • 1
    Makes sense. Thanks for the feedback/info! – lsimonetti Aug 08 '17 at 14:02
  • Keep in mind that your approach won't work with PropTypes properly because this `this.mouseEnterBehaviorEnabled() && this.mouseEnter` returns a boolean, the `PropTypes.func` definition for `onMouseEnter` will throw an error for the wrong type of prop supplied. Thus, why you should pass in an empty function when the condition is `false` (the `noop` approach, f.e.). – Denialos Aug 08 '17 at 14:34
  • @Denialos I think I see where you are coming from, but I believe your suggestion is wrong. The problem only appears for when a prop is defined as **required** (e.g `myFunc: PropTypes.func.isRequired`). You are referring to the standard mouse event `onMouseEnter` which is not a required prop. As such, passing `null`, `false`, `undefined`, etc. is completely valid. – Chris Aug 08 '17 at 15:13
1

You can do it by using the spread operator (ES6), and logical AND short circuit evaluation:

...{ onClick: condition && this.handleClick }

Example:

const arr = ["1", "2", "3"];

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  handleClick = e => {
    console.log(e.target.innerHTML);
  };

  render() {
    return (
      <div>
        {arr.map(o => {
          const myProps = {
            ...{ onClick: o != "1" && this.handleClick }
          };
          return (
            <li {...myProps}>
              {o}
            </li>
          );
        })}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Sagiv b.g
  • 30,379
  • 9
  • 68
  • 99
0

You can use this in JSX, I don't think this is the problem.

If this is the problem, it may be inside your method renderMouseEvents, did you bind it to this?

But I think it does not work because of what your method renderMouseEvents is returning. Though, it is possible to inject properties, check this out, it may help:

Dynamic attribute in ReactJS

sjahan
  • 5,720
  • 3
  • 19
  • 42
  • No, he just can't insert JS in a jsx html-like TAG – Nevosis Aug 08 '17 at 13:45
  • 1
    In the link I posted, it seems to work: `return ;` button is an HTML tag and it receives some props which could be returned by a method, couldn't it? I've done it before and it works like a charm. – sjahan Aug 08 '17 at 13:54
0

If I understand what you're asking, I believe the problem you are running into has to deal with how JSX transforms into JavaScript.

<el arg1="1" arg2="2">3</el>

is translated to something similar to the following:

React.createElement('el', { arg1: '1', arg2: '2' }, '3');

What you have written is likely attempted to be translated like this, assuming your renderMouseEvents() returns { onMouseEnter: this.mouseEnter }:

React.createElement('li', { { onMouseEnter: this.mouseEnter } }, '...');

This is invalid JavaScript. The compiler is telling you it is expecting ... (JavaScript spread syntax), which will likely resolve this issue.

As noppa mentioned, there may be better ways to achieve what you want. Can you be more specific about the problem you are facing?

Zachary Kuhn
  • 1,152
  • 6
  • 13