DEV Community

Cover image for Simplify condition rendering in React.js

Simplify condition rendering in React.js

Redžep Murati on July 08, 2021

As a professional developer you are forced to stay up to date with the latest trends in technology. This year I've added Svelte to my bucket list a...
Collapse
 
chrisczopp profile image
chris-czopp

Nice one! If you're interested in exploring other frameworks, in SolidJS you have:

function Show<T>(props: {
  when: T | undefined | null | false;
  fallback?: JSX.Element;
  children: JSX.Element | ((item: T) => JSX.Element);
}): () => JSX.Element;
Enter fullscreen mode Exit fullscreen mode

...which you can use like:

<Show when={state.count > 0} fallback={<div>Loading...</div>}>
  <div>My Content</div>
</Show>
Enter fullscreen mode Exit fullscreen mode

I find really neat.

Collapse
 
chetan_atrawalkar profile image
Chetan Atrawalkar

How to write this type of code(like second) in Dev Community post please help

Collapse
 
chrisczopp profile image
chris-czopp

Do you mean syntax highlighting? If so, then like this:

Alt Text

It's just Mardown compatible with Github.

Thread Thread
 
chetan_atrawalkar profile image
Chetan Atrawalkar

Yes but how please share image details

Thread Thread
 
rmurati profile image
Redžep Murati

Take a look at this official markdown guide:
markdownguide.org/extended-syntax/

Thread Thread
 
edgarmendozatech profile image
Edgar Mendoza • Edited

@chetan_atrawalkar just surround your code with the following:

Example highlighting

console.log("Hello World");
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
chetan_atrawalkar profile image
Chetan Atrawalkar

Thank you❤️

Collapse
 
nemit74180 profile image
nemit74180

hi

Collapse
 
rmurati profile image
Redžep Murati

Nice tip. Thank you ❤️

Collapse
 
miketalbot profile image
Mike Talbot ⭐ • Edited

Nice, I do something similar - it's also cool to add then and else as props. I also do this with switch:

import React, { useContext, useState } from "react"

const SwitchContext = React.createContext()

function ensureArray(ar) {
    return Array.isArray(ar) ? ar:[ar].filter((f) => f!==undefined)
}

function noop() {}

export function Switch({ value, children }) {
    const [switchContext] = useState({ cases: {} })
    switchContext.value = value
    return <SwitchContext.Provider value={switchContext}>{children}</SwitchContext.Provider>
}


export function Case({ when, children, execute = noop }) {
    const toCheck = ensureArray(when)
    const { value, cases } = useContext(SwitchContext)
    let condition = toCheck.some((when) => {
        if (typeof when === "function") {
            return when(value)
        } else {
            return when === value
        }
    })

    cases["" + when] = condition
    if (condition) {
        execute()
        return <>{children}</>
    } else {
        return null
    }
}

export function CaseElse({ children }) {
    const { cases } = useContext(SwitchContext)
    if (!Object.values(cases).some((v) => !!v)) {
        return <>{children}</>
    }
    return null
}

Enter fullscreen mode Exit fullscreen mode

I use it like this:

 <Switch value={column.type}>
            <Case when="action">
                <TableCell className={classes.tight} onClick={prevent(noop)}>
                    <LoadedAction column={column} item={item} />
                </TableCell>
            </Case>
            <Case when={["more", "than", "one", "reason", ()=>column.name === "including this!"]}>
                   {/* Some other cell */}
            </Case>
            {/* Other cases */}
            <CaseElse>
                <TableCell className={classes.tight}>
                    <LoadedColumn column={column} item={item} />
                </TableCell>
            </CaseElse>
</Switch>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
lonlilokli profile image
Lonli-Lokli

I've tried it, it's pretty neat but do you know how to limit type with React context? Ie to make Typescript do not complain about missing props

stackblitz.com/edit/react-ts-6ynx8...

Collapse
 
miketalbot profile image
Mike Talbot ⭐

I tried the link but didn't see any lint issues etc, what is it complaining about?

Collapse
 
lonlilokli profile image
Lonli-Lokli

Nice, but you are missing Default case when no other matches

Collapse
 
miketalbot profile image
Mike Talbot ⭐ • Edited

Nope, I just called it CaseElse (my project has a ton of things called DefaultXYZ so it doesn't scan well).

Collapse
 
rmurati profile image
Redžep Murati

Oh wow this looks interesting. Thank you for sharing :)

Collapse
 
baronw profile image
Baron Willeford

Your approach has an issue: the children are still being rendered, they’re just not being displayed. So if you have a component that, say, makes a network request when it mounts, that’s still going to happen, which is likely not what you want.

Collapse
 
florianrappl profile image
Florian Rappl

This is wrong. It's true that the elements are still constructed, but nothing is mounted and since React only goes in top-bottom the non-rendering / non-mounted children will never be looked at.

Look at this example.

   const IF = ({ condition, children }) => {
     if (!condition) return null;
     return <React.Fragment>{children}</React.Fragment>;
   };

   const Example = ({name}) => {
    console.log('Render Example', name);
    return <div />;
   };

ReactDOM.render(<div><IF condition={false}><Example name="false - not displayed" /></IF><IF condition={true}><Example name="true - displayed" /></IF></div>, document.querySelector("#app"))
Enter fullscreen mode Exit fullscreen mode

What you'll see in the console is:

"Render Example", "true - displayed"
Enter fullscreen mode Exit fullscreen mode

(Note there is no false - displayed shown.)

So there shouldn't be any such thing as a network request (which anyway shouldn't appear there, because these things are side-effects and, e.g., useEffect would only be called on mounted components anyway).

The only issue with this approach is that you construct more objects (i.e., React elements) than you would otherwise. This becomes only an issue when you have a larger render tree spawning from there.

Collapse
 
rmurati profile image
Redžep Murati

Exactly 🙂 Thank you for detailed clarification.

Thread Thread
 
trusktr profile image
Joe Pea

No issue with extra elements in Solid, no matter how big the app gets. Check it out!

solidjs.com

Collapse
 
hedwardd profile image
Heath Daniel

Interesting. Can you explain further? They are rendered by the parent component but the 'IF' component just controls whether they are displayed or not?

Collapse
 
baronw profile image
Baron Willeford

Correct. ‘IF’ and the child are rendered from the same place. The ‘children’ prop is just a reference, is not actual call site. To actually prevent it from being rendered, you would want to call the conditionally rendered component from the ‘IF’.

Thread Thread
 
hedwardd profile image
Heath Daniel

I'm not sure if this is a good way to test but I tried testing it by creating a component that logs to the console when it renders and it looks like that code never ran.

Link to CodePen: codepen.io/hedwardd/pen/xxdOpxy?ed...

Could it have something to do with it being a functional component?

Thread Thread
 
lfbergee profile image
Leiv Fredrik Berge

Exactly, this will actually add the child node to the tree and perform any effects for example. You can avoid this by using render props, but that kinda defeats the purpose.

Thread Thread
 
hedwardd profile image
Heath Daniel

Does it? Now I'm confused. If you look at the CodePen I created, the effects don't seem to run (unless I'm misunderstanding something)

Thread Thread
 
rmurati profile image
Redžep Murati

Actually it shouldn't run. I've created a codesadbox for you guys to check out where you can see both functional and class based components.
Here is the link:
codesandbox.io/s/nice-wiles-cwfi6?...

Collapse
 
christoslitras profile image
Christos Litras • Edited

IMO there is no need to create an <IF> component. For most simple cases, ternary operator <condition> ? <if-true> : <if-false> or simple boolean operators && and/or || will do the job; for something more complex, it would be better to create an additional function to render it. If we want to render it directly in place and we have many conditions/components to check, there is a new Javascript do expression (tc39/proposal-do-expressions) that is in stage 1 of the TC39 process and it will solve such problems by using some native syntax like:

const Component = props => (
  <div className="myComponent">
    {do {
      if (color === "blue") {
        <BlueComponent />;
      } else if (color === "red") {
        <RedComponent />;
      } else if (color === "green") {
        <GreenComponent />;
      }
      <DefaultColorComponent />;
    }}
  </div>
);
Enter fullscreen mode Exit fullscreen mode

We can already use it by using Babel plugin @babel/plugin-proposal-do-expressions.

Collapse
 
jfullerco profile image
jfullerco

Figured I’d share one of the first use cases I found for this and it’s one I believe could be helpful to others. That’s in hydrating state with React Router useHistory state in the event of a browser refresh.

import React from react
const CheckIfCacheNeeded = ({
   value, 
   setValue, 
   fallbackValue, 
   handleRestore, 
   children }) => {
   value != undefined ? value : 
   handleRestore(fallbackValue, setValue)
 return(
   {children}
  )
}
export default CheckIfCacheNeeded
Enter fullscreen mode Exit fullscreen mode

When calling the component you’d need to add a handleRestore function that took the fallbackValue and setter function as parameters.

It works well with React Router useHistory state that persists during browser refresh.

Collapse
 
jfullerco profile image
jfullerco

This is a question, not a critique, is it not necessary to check for the existence of the conditional variable if you use the NOT operator?

Fwiw, great post! Definitely will use this as inspiration for some component refactoring on my current project.

Thanks!!

Collapse
 
rmurati profile image
Redžep Murati

Hello, no actually you don't have to every falsy value is considered as false.

Collapse
 
jfullerco profile image
jfullerco

Thanks! That’s super helpful to know!

Collapse
 
codyseibert profile image
Cody Seibert

To each their own, but I argue this is a unnecessary abstraction. It’s like creating an add() function that adds numbers when you can do a + b in code.

Collapse
 
rmurati profile image
Redžep Murati

For me honestly it was more of a stylistic choice. I like when my jsx resembles closely HTML

Collapse
 
urielbitton profile image
Uriel Bitton

this is pretty genius. good job. WIll be using. thanks.

Collapse
 
rmurati profile image
Redžep Murati

Thank you ❤️

Collapse
 
jackmellis profile image
Jack

Personally I swear by jsx-control-statements
I use it pretty much every day for conditional rendering

Collapse
 
rmurati profile image
Redžep Murati

I wasn't aware of this. Thanks for sharing 🙂

Collapse
 
nemit74180 profile image
nemit74180

hi

Collapse
 
rmurati profile image
Redžep Murati

👋