2

I have a render function like this one:

render() {
    const statement = true
    return (
      <div>
        {
          statement &&
          <div>
            <p>
              {this.buildStuff()}
            </p>
            <p>
              {this.buildStuff()}
            </p>
            <p>
              {this.buildStuff()}
            </p>
          </div>
        }
      </div>
    );
  }

To avoid calling buildStuff() three times, I would like to assign it to a variable. How can I declare a variable after the line with statement &&?

A quick solution would be to do

const statement = true
const stuff = statement ? buildStuff() : null;

but this solution use two branches instead of one.

You can try this code on StackBlitz.

This what it would look like in Razor.

aloisdg
  • 22,270
  • 6
  • 85
  • 105
  • What's wrong with `const stuff = statement ? buildStuff() : null;`? – hindmost Jan 16 '19 at 09:57
  • Why can't you do it before the return statement `const ui = statement && this.buildStuff(); return ... // just use ui here ` – Yury Tarabanko Jan 16 '19 at 09:58
  • @hindmost why make two statements when one should be enough? – aloisdg Jan 16 '19 at 10:03
  • @aloisdg Then use `&&` as @Yury Tarabanko suggested – hindmost Jan 16 '19 at 10:04
  • @YuryTarabanko you are still checking the bool statement twice. – aloisdg Jan 16 '19 at 10:06
  • If you meant checking `statement` variable, you may replace its usage in JSX with `stuff`: `stuff && ...` – hindmost Jan 16 '19 at 10:10
  • @aloisdg You could check ui instead. :) Variable declaration is a statement you can't use it inside an expression you want to return. You could declare `let stuff ;` before and the use comma operator `{statement && (stuff == this.buildStuff(), (
    {stuff}
    )}` but this is less readable.
    – Yury Tarabanko Jan 16 '19 at 10:10
  • @hindmost I am not sure to understand. I am new to react. Could you details or provide an example? – aloisdg Jan 16 '19 at 10:11
  • @YuryTarabanko not a bad idea! It smells a bit more like a work around than a real solution but the idea is here. I wont sacrifice readability for the perf gain of a bool (which is almost nothing) – aloisdg Jan 16 '19 at 10:13
  • @aloisdg I do NOT recommend it. `const stuff = statement && this.buildStuff()` is good enough. The `buildStuff` function would be called 0 or 1 times. Checking boolean `statement` to be truthy costs nothing. Do not micro optimize it. – Yury Tarabanko Jan 16 '19 at 10:15
  • @YuryTarabanko Yes of course, but if I can avoid it without any sacrifice I will do it. In razor for example, this is [easy to achieve](https://stackoverflow.com/a/42932166/1248177) – aloisdg Jan 16 '19 at 10:18
  • 1
    @aloisdg You would sacrifice readability and gain nothing. Really nothing. BTW I bet Rajesh's solution is less performant than checking a boolean :). It creates an array, temp object, anonymous functions that would be gced, and adds yet another function call. – Yury Tarabanko Jan 16 '19 at 10:23

4 Answers4

3

You can try something like this as well:

  • You can create a function that deals with this UI representation.
  • In this function, you can call buildStuff and have it return 3 <p> tags.
  • Then in main render, you can check your condition and render accordingly. This will make your render clean and declarative.
getBuildJSX() {
  const stuff = this.buildStuff();
  return Array.from({ length: 3}, () => <p> { stuff }</p>);
}

render() {
  const statement = true
  return (
    <div>
      {
        statement ? this.getBuilsJSX() : null
      }
    </div>
  );
}

Try it online

aloisdg
  • 22,270
  • 6
  • 85
  • 105
Rajesh
  • 24,354
  • 5
  • 48
  • 79
  • Nice idea. Since we cannot put a variable in the JSX, we use a function to wrap the logic and move the variable in it. – aloisdg Jan 16 '19 at 10:15
  • @aloisdg Thanks for adding a sample. Its not opening for me, but should help others – Rajesh Jan 16 '19 at 10:22
1

First solution (edit: alternative)

render() {
    const statement = true;
    const stuff = this.buildStuff(statement, 3); // jsx result looped in divs
    return (
      <div>
        {
          statement &&
          <div>
            { stuff }
          </div>
        }
      </div>
    );
  }

Alternative, memoization (caching of functions) if this is your goal:

const memoize = require('fast-memoize');
const memoized = memoize(this.buildStuff);


...

render() {
    const statement = true; 
    return (
      <div>
        {
          statement &&
          <div>
            <p>
              {memoized()}
            </p>
            <p>
              {memoized()}
            </p>
            <p>
              {memoized()}
            </p>
          </div>
        }
      </div>
    );
  }

The true power of memoization however is, if you cache based on the parameter you give to buildStuff (maybe you move statement into buildstuff?). In your case I would just clean up the component and parameters in favour of readability rather than optimising something. So last alternative:

// Stuff is a component now
const Stuff = ({statement, stuff}) => {
  if(!statement)
    return null;

  const result = stuff();

  return (
    <div>   
      <p>
        {result}
      </p>
      <p>
        {result}
      </p>
      <p>
        {result}
      </p>
    </div>
  )
}

render() {
    return (
      <div>
        <Stuff statement={true} stuff={this.buildStuff} />
      </div>
    );
  }

The benefit, you can now choose to pass the result or the function itself through props, and in the downward component either call the function or simply have its results passed through.

Single answer to your question in the headline: you cant, JSX is not a templating engine like Razor.

Explanation:

// JSX  
<div id="foo">Hello world</div>

// Translates into 
React.createElement("div", {id: "foo"}, "Hello world");

// JSX
<div>{ statement && <div /> }</div>

// Translates to
React.createElement("div", null, statement && React.createElement("div"));

Either you declare a new variable with an attribute, or you simply cant, since javascript does not allow variable creation inside parameters of functions.

Tom Siwik
  • 992
  • 9
  • 22
0

I think one of the main ideas of react is to use components to structure your code.

So one way to do it would be like this:

render() {
    const statement = true;

    const Stuff = ({statement}) => {
      if (!statement) { return null; }
      return this.buildStuff();
    }

    return (
      <div>
        <p>
          <Stuff statement={statement} /> 
        </p>
        <p>
          <Stuff statement={statement} />
        </p>
        <p>
          <Stuff statement={statement} />
        </p>
      </div>
    );
  }

Updated StackBlitz.

Max
  • 1
  • 1
0

This answer is an answer to the problem but not a solution to the question. If you cannot declare a variable inside brackets in react (as you could do in Razor for example). Calling twice a statement can still be your best bet.

render() {
    const statement = true
    const stuff = statement ? this.buildStuff() : null
    return (
      <div>
        {
          statement &&
          <div>
            <p>
              {stuff}
            </p>
            <p>
              {stuff}
            </p>
            <p>
              {stuff}
            </p>
          </div>
        }
      </div>
    );
  }

At least, we call this.buildStuff() only if needed and if we do, we call it only once.

aloisdg
  • 22,270
  • 6
  • 85
  • 105
  • Check in this: `const stuff = statement ? this.buildStuff() : null` is redundant. `stuff` will only be used in truthy case. Only benefit of this would be that, for falsey value, you are not calling function at all. But having an extra check doesn't make that much sense – Rajesh Jan 16 '19 at 12:45