Sushmit Sagar
Sushmit Sagar

Reputation: 1508

Are these two logical operator conditions same?

I have a react component which renders google map and also fetches data from an api. The google map dependency is loaded asynchronously when the component renders.

initial state:

state = {
  isFetching: true
}

The component is wrapped in a HOC named withAsyncDependencies:

import React from 'react';
import { isEmpty } from 'lodash';

// local dependencies
import { loadApi as loadMapApi } from '../../utils/googleMapLoader';

// global dependency store
const dependencies = {
    google: null
};

/**
 * @desc This HOC loads third-party JavaScript dependencies asynchronously.
 * @example
 * import { withAsyncDependencies } from 'ui/hoc/withAsyncDependencies';
 */
const withAsyncDependencies = ( Component ) => {
    class WithAsyncDependencies extends React.Component {
        constructor( props ) {
            super( props );

            // component state
            this.state = {
                isLoading: true
            };

            // load async dependencies
            this.loadDependencies();
        }

        /**
         * @desc Loading asyn
         */
        loadDependencies() {
            log.debug( 'WithAsyncDependencies.loadDependencies()' );

            // load async dependencies which are missing from the global store
            Promise.all( [
                isEmpty( dependencies.google ) ? loadMapApi() : dependencies.google
            ] ).then( ( [ google ] ) => {

                // add dependencies to global store
                dependencies.google = google;

                // update state
                this.setState( {
                    isLoading: false
                } );
            } );
        }

        /**
         * @desc Render component
         */
        render() {
            return (
                <Component
                    { ...this.props }
                    isLoadingAsyncDependencies={ this.state.isLoading }
                    asyncDependencies={ dependencies }
                />
            );
        }
    }

    // set display name
    WithAsyncDependencies.displayName = 'WithAsyncDependencies';

    // set default props
    WithAsyncDependencies.defaultProps = {};

    // return class
    return WithAsyncDependencies;
};

export { withAsyncDependencies };

So now the HOC passes down props isLoadingAsyncDependencies & asyncDependencies

Now I want to show a preloader while this.state.isFetching is true and props.asyncDependencies.google is not null.

I ran into an issue when I disconnected from an internet and my app threw errors in console as dependency google hadn't loaded. But state isFetching gets set to false after the API response as both FE & BE are running on my localhost.

There are two routes google maps are required. To solve this issue one of my colleague used following condition on one page:

isReady = () => {
    const asyncDependenciesLoaded = false === this.props.isLoadingAsyncDependencies;
    const { google } = this.props.asyncDependencies;

    if ( asyncDependenciesLoaded && google ) {
        this.setState( { isReady: true } );
    }
}

componentDidUpdate() {
    if ( !this.state.isReady ) {
        this.isReady();
    }
}

render () {
  const showPreLoader =  !( !this.state.isFetching && this.state.isReady );

      return (
        <div className="providers-container">
            <ProvidersPageView
                isFetching = { showPreLoader }
            />
        </div>
    );
}

And my solution on another page is as follows:

render () {
  const showPreLoader =  this.state.isFetching || !this.state.isReady;

      return (
        <div className="providers-container">
            <ProvidersPageView
                isFetching = { showPreLoader }
            />
        </div>
    );
}

Both the conditions are working fine. I am wondering if both the conditions
!( !this.state.isFetching && this.state.isReady ) and this.state.isFetching || !this.state.isReady
are technically same?

which condition is more reliable and why? Also does it matter if I switch the positions of variables in second condition like so: !this.state.isReady || this.state.isFetching ?

Upvotes: 0

Views: 82

Answers (1)

derpirscher
derpirscher

Reputation: 17382

  1. Yes, !(!this.state.isFetching && this.state.isReady) is logically equivalent to this.state.isFetching || !this.state.isReady because of de Morgan's laws. What do you mean by "more reliable"? They are mathematically equivalent, thus none of them is "more reliable". But probably the second one is more readable, because you don't need parenthesis and only have a single negation.

  2. Yes, mathematically speaking the order of operands in with && and || doesn't matter, because a || b == b || a and a && b == b && a.

But as most programming languages have short circuit evaluation of logical operations, you have to be careful, if your operands a and b have side-effects, which you rely on ...

Ie, a && b won't evaluate b if a is already false, because the value of b is irrelevant and the expression can never become true.

And a || b won't evaluate b if a is already true, because the value of b is again irrelevant and the expression will always be true.

So if a and b are not values but functions, and you rely on all of them to be executed, you may get a problem.

Imagine the following (pseudo)code

function a() {
  this.prop = somevalue;
  return false;
}

function b() {
  this.otherprop = othervalue;
  return true;
}


function c() {
  if (a() && b()) 
    dosomething();
}

In this code, b will never be executed, thus this.otherprop will probably never be set ...

Upvotes: 1

Related Questions