0

I've been adopting ReactJS + Redux in my projects for a couple of years. I often end up in asynchronous situations where I need my component to wait for the state to be updated to render. Normally the simple logic !this.props.isFetching ? <Component /> : "Loading..." is enough.

However there are cases where I need to check for the state of an array that is embedded in the state object. In these cases, most of my components end up looking like this:

  renderPostAuthor = () => {
    if (!!this.props.postDetails.author) {
      return this.props.postDetails.author[0].name;
    } else {
      return (
        <div>
          <StyledTitle variant="subheading" gutterBottom color="primary">
            Loading...
          </StyledTitle>
        </div>
      );
    }
  };

Is this use of the !! notation a good pattern / practice in ReactJS?

UPDATE: Thanks for the responses, and they are all valid. Perhaps, to clarify my question further, note that this.props.postDetails is a state itself that contains a number of objects and arrays. Therefore the problem is that if I omit the !! and this.props.postDetails isn't instantiated yet, and hence contains no arrays such as author[], I get the undefined error.

James
  • 3,597
  • 11
  • 41
  • 76
  • It's not wrong but it's also not necessary, since leaving out `!!` will behave in exactly the same way. Basically there's an *implicit* `!!` in the normal way that an `if` test works. – Pointy Jun 29 '18 at 13:39
  • Don't know about best practice using react, but this is just for making the variable a `boolean` type. – lealceldeiro Jun 29 '18 at 13:39
  • 1
    There is nothing wrong with `!!`, but `if (this.props.postDetails.author)` would have the same effect in this case. It's more a question of taste, unless you explicitly need a `boolean`. – Tholle Jun 29 '18 at 13:39
  • @Pointy not the same way, look. `var a = 1; var x = !!a; console.log(x) // true (not 1)` – lealceldeiro Jun 29 '18 at 13:40
  • 1
    @lealceldeiro of course, but the code posted is using `!!` in an `if` test. – Pointy Jun 29 '18 at 13:41
  • Please check the update I made on the question. Sometimes I still need to check whether `this.props.postDetails` has rendered before I can even test for `this.props.postDetails.author` array. – James Jun 29 '18 at 14:31

2 Answers2

5

This has much more to do with just JavaScript in general than with React.

No, that use of !! isn't particularly useful. This:

if (!!this.props.postDetails.author) {

is the same as this:

if (this.props.postDetails.author) {

Neither of them means that author contains an array with at least one entry, which your next line of code is relying on. To do that, add .length or, with your particular example, probably [0] instead (in case author had an entry, but that entry was a falsy value):

if (this.props.postDetails.author[0]) {

If author may be null or undefined, we need to do two checks:

if (this.props.postDetails.author && this.props.postDetails.author[0]) {

Since we're going to use the result, it may be best to save the result to a variable or constant:

const firstAuthor = this.props.postDetails.author && this.props.postDetails.author[0];
if (firstAuthor) {
    return firstAuthor.name;
}

Example of the current code throwing an error:

console.log("Running");
const author = [];
if (!!author) {
  console.log(author[0].name);
} else {
  console.log("No Author");
}

Example of checking [0] when we know author won't be null/falsy:

console.log("Running");
const author = [];
if (author[0]) {
  console.log(author[0].name);
} else {
  console.log("No Author");
}

Example of the double-check when author may be null/falsy:

console.log("Running");
const author = null;
if (author && author[0]) {
  console.log(author[0].name);
} else {
  console.log("No Author");
}

Example of saving and using the result:

function example(author) {
  const firstAuthor = author && author[0];
  if (firstAuthor) {
      return firstAuthor.name;
  } else {
      return "Loading...";
  }
}
console.log(example(null));                      // Loading...
console.log(example([]));                        // Loading...
console.log(example([{name:"Harlan Ellison"}])); // "Harlan Ellison" (RIP)
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • The problem is that if I omit the `!!` and `this.props.postDetails` isn't instantiated yet, and hence contains no arrays such as `author[]`, I get the `undefined` error. – James Jun 29 '18 at 14:28
  • @JamesJ. - You'd have that problem *with* the `!!`, too. If `this.props.postDetails` may not exist yet, you need a further guard: `if (this.props.postDetails && this.props.postDetails.author && this.props.postDetails.author[0]) {` If you can assume `.author` will exist if `.postDetails` does, you could leave out the middle one. The answers to [this](https://stackoverflow.com/questions/19179373/) and [this](https://stackoverflow.com/questions/19718545/) may also help. – T.J. Crowder Jun 29 '18 at 14:35
1

There are times in react when using the !! is particularly helpful, but this is not the instance as stated above. The most common case I've found is when evaluating whether you're going to render array items or not. Often people will use the length of the array to decide whether to work with it or not since 0 length is a falsey boolean:

render () {
  return this.props.array.length && <MyList items={this.props.array} />
}

Unfortunately this will return the 0 which will be rendered on the page. Since false will not render on the page a good alternative would be to use the double bang so that false is returned.

render () {
  return !!this.props.array.length && <MyList items={this.props.array} />
}
tenor528
  • 1,120
  • 13
  • 20