0

I'm trying to get multiple layers of conditional rendering to work in react and am running into problems.

I've simplified my usecase to the code below.

{data &&
  dataIsLoading
  ? <h1> Loading <h1>
  : !data.isError
    ? <Component data={data} />
    : <h1> Error <h1>
}

The behavior I'd expect from the above snippet is

  1. Check if the data exists - if so
  2. Check if the data is loading - if so display 'loading', if not
  3. Check if the data has an error - if so display 'error', if not
  4. Display the data

Unfortunately when running this code I get a Null reference exception when trying to evaluate !data.isError. That line shouldn't be executed if data is null - that's the point of the conditional rendering!

I've tried explicitly checking with data !== null &&, and I've tried replacing {data && with simply {false && and I still run into the same problem. If I'm misunderstanding how react conditional rendering works how can I safely guard against null values in this context.

4
  • undefined ? 'a' : 'b' returns me 'b' - so !data.isError is on the "false" path Commented Aug 9, 2021 at 13:35
  • 2
    You need () around everything after data && if you want that. The data && dataIsLoading is only used by the first conditional. If that's false (because data is null), the third operand of the conditional (starting with !data.isError) is evaluated. So: {data && (dataIsLoad ? ... : ...)}. Commented Aug 9, 2021 at 13:36
  • (It seems slightly odd, though, that data would be non-null when dataIsLoading is true.) Commented Aug 9, 2021 at 13:39
  • You could try to separate those ternary expressions so it would make your life and other developer's life a bit happier, also you could clearly see the issue that you are encounter. It's not recommended by eslint rules: eslint.org/docs/rules/no-nested-ternary Commented Aug 9, 2021 at 13:39

3 Answers 3

2

Nested conditionals can be tricky to read. The conditional operator accepts three operands:¹

  1. The condition
  2. The operand to evaluate if the condition is true
  3. The operand to evaluate if the condition is false

Your outermost condition's operands are:

  1. data && dataIsLoading
  2. <h1> Loading <h1>
  3. !data.isError ? <Component data={data} /> : <h1> Error <h1>

Note that the data && part is only part of the condition of your outermost conditional, not the one you have in your nested conditional. So when data && dataIsLoading is false (because data is null), !data.isError ? <Component data={data} /> : <h1> Error <h1> is evaluated — and fails, because data is null.

The minimum-changes fix is to put () around everything after data &&:

{data && (
  dataIsLoading
  ? <h1> Loading <h1>
  : !data.isError
    ? <Component data={data} />
    : <h1> Error <h1>
)}

...but you might consider breaking that up in code prior to this JSX expression instead, to make it simpler to read and maintain.


¹ Which is why it's sometimes called "the ternary operator" (a unary operator accepts one operand, a binary operator accepts two, a ternary accepts three, ...). It happens to be JavaScript's only ternary operator, for now, but that could change.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, I appreciate the feedback. That helped me understand!
0

I put together a small sandbox which shows some of the different states that you can run into: https://codesandbox.io/s/epic-tereshkova-g60xy The important bits are below. When you check for data && dataIsLoading and this returns false, then you go to the next check !data.isError but if data = undefined then data.isError will throw an error. You can expand this condition to check if data exists and then calculate the !data.isError.

<div className="App">
  {data && dataIsLoading ? (
    <h1> Loading </h1>
  ) : data && !data.isError ? (
    <h1> All good </h1>
  ) : (
    <h1> Error </h1>
  )}
</div>

Comments

0

You said that you're using React, right? So, let me give to you my 5 cents about this. For me, this code below is the better way to write this:

{
   dataIsLoading ? (
     <h1>Loading</h1>
   ) : (
     <>
       {
         data.isError ? (
           <h1>Error</h1>
         ) : (
           <Component data={data} />
         )
       }
     </>
   )
}

...on this way, your code it's much more understandable and you can remove the double check directly, because the first will check the loading and the second, inside the fragment, will do the data check.

PS: Another VERY IMPORTANT THING...I saw in your code, lots of tags unclosed, like the H1 tags. This will return in troubles every time. I recommend you to use the EsLint or Prettier to help you with this.

2 Comments

Adding parens can indeed be helpful. Note that this uses dataIsLoading to show a loading indicator even if data is null, which the OP's code didn't (which might have been on purpose). Also, there's no reason for the fragment and the nested JSX expression, <>{x}</> is the same as x in this context.
For me, the data is not important if loading is true. So, this check can be done separately. And the fragment will help the next developers, that will do this code maintenance, to understand the structure more fast. Works for me, but this is only a tip.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.