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👉CA.

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👉CA.

Yes but how please share image details

Thread Thread
rmurati profile image
Redžep Murati Author

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👉CA.

Thank you❤️

Collapse
nemit74180 profile image
nemit74180

hi

Collapse
rmurati profile image
Redžep Murati Author

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 Author

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 Author

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 Author

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 Author

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
jfbrennan profile image
Jordan Brennan

I am reminded yet again why I switched to Vue and Riot.

<button v-if="user.loggedIn">Log out</button>

<button if="{user.loggedIn}">Log out</button>
Enter fullscreen mode Exit fullscreen mode
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 Author

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 Author

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 Author

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

Collapse
nemit74180 profile image
nemit74180

hi

Collapse
rmurati profile image
Redžep Murati Author

👋