11 ms·
Advice on JSX Conditionals
- eyelidlessness 5y agoIt doesn’t necessarily have the same benefits in React, but folks ought to consider using components for control flow, like SolidJS does[1]. 1: https://www.solidjs.com/docs/latest/api#control-flow https://www.solidjs.com/docs/latest/api#control-flow
- Eric_WVGG 5y agoThat's pretty slick.
- dimgl 5y agoFunnily enough, this has existed in React through a Babel plugin. I'm not sure why this hasn't really caught on. https://github.com/AlexGilleran/jsx-control-statements https://github.com/AlexGilleran/jsx-control-statements
- eyelidlessness 5y agoI’ve encountered that, and kind of don’t understand why you’d need a Babel plugin. They’re trivial components to implement in regular JSX.
- dimgl 5y agoThat's what I was thinking too!
- latchkey 5y agoSome people consider this added complexity? https://news.ycombinator.com/item?id=30509806 https://news.ycombinator.com/item?id=30509806
- eyelidlessness 5y agoI mean, sure. But compilers often impose complexity as a trade off for performance or other benefits.
- tdumitrescu 5y agoControl-flow components like "<For>"... because you couldn't stand just writing "for" or "if" like a caveman and had to invent a custom DSL for it.
- eyelidlessness 5y agoSolid actually uses them for static analysis which ~isn’t otherwise possible in JSX, which has to be an expression. It would be great if for/if were expressions in JS, but they’re not.
- kipple 5y agoAgreed. I particularly like the look of their `<Switch>/<Match>` component[1]: <Switch fallback={<div>Not Found</div>}> <Match when={state.route === "home"}> <Home /> </Match> <Match when={state.route === "settings"}> <Settings /> </Match> </Switch> Which doesn't seem to have an analog in the React babel-plugin[2] or standalone lib[3] [1] https://www.solidjs.com/docs/latest/api#%3Cswitch%3E%2F%3Cmatch%3E https://www.solidjs.com/docs/latest/api#%3Cswitch%3E%2F%3Cma... [2] https://github.com/AlexGilleran/jsx-control-statements https://github.com/AlexGilleran/jsx-control-statements [3] https://github.com/samuelneff/react-control-flow https://github.com/samuelneff/react-control-flow
- 14u2c 5y agoYou could very easily implement that pattern yourself in a react app as a few HoCs. So far I think the react project has done a pretty decent job at limiting the surface area of the API. This seems a bit high level.
- molf 5y agoI've always found these approaches to writing conditions in JSX to be terrible. Wouldn't it be nice if JavaScript were an expression-oriented language? Then you could just write: {if (gallery.length) { <Gallery slides={gallery}> }} There has been a "do expressions" proposal [0] for many years, which addresses this (though it is more verbose). I hope it will be accepted some day. [0] https://github.com/tc39/proposal-do-expressions https://github.com/tc39/proposal-do-expressions
- 9wzYQbTYsAIc 5y agoFrom the article: “{number && <JSX />} renders 0 instead of nothing. Use {number > 0 && <JSX />} instead.” Functional JSX would look like: const isNumber = number > 0; {isNumber && <JSX />} You can similarly do things like: const isVisible = condition1 && (condition2 || condition3) || guard(props.input1); {isVisible && <JSX />} The functional paradigm and some basic code factoring can make quick work of conditional JSX
- redler 5y agoAnother quick option for avoiding the "zero" issue: {!!number && <Thing />}
- 9wzYQbTYsAIc 5y agoOne of the joys of JavaScript, right there: not-not sorry.
- dbetteridge 5y agoBetter to use Boolean(number) Its more obvious what it achieves just at a glance.
- postalrat 5y ago"!!" should be pretty obvious to any javascript developer.
- Kaotique 5y agoI actually really like the nested ternaries. TypeScript understand them very well and you don't have to extract your code outside the render to start using if/else structures. They also format quite well.
- eyelidlessness 5y agoI like ternary conditionals, nested or otherwise. But I also like reduce. And regular expressions. And I know a lot of other devs hate some or all of them. So I tend to use them sparingly when working with a team… although I do see more ternaries in idiomatic code these days, generally as devs embrace a more FP style.
- phelm 5y agoThanks, this is really useful. I’ve been using this Babel plug-in found it quite intuitive https://github.com/AlexGilleran/jsx-control-statements https://github.com/AlexGilleran/jsx-control-statements
- tuyiown 5y agoI've learnt most of those the hard way, event though TSX is a great safe guard because I tend to slip. I find funny the persistance in sidestepping a `.length > 0` at all costs.
- eyelidlessness 5y agoPersonally I hate implied casting of booleans (and implied casting generally), and go out of my way to make boolean checks explicit whenever I touch the code. These kinds of shortcuts made some sense when minification was commonly a manual thing. But now, I want code to be as verbose as necessary for it to be unambiguous.
- nkohari 5y agoI've always much preferred using local variables to hold conditional fragments. Since React will safely ignore null/undefined, you can do: let child; if (someCondition) { child = <SomeComponent ... />; } return ( <div> Maybe here's a child: {child} </div> ); This lets you avoid embedding conditional logic in your (already pretty dense) JSX tree.
- postalrat 5y agoI do this sometimes but having to look up where child was defined and everything that could have changed it can be tiresome. That's why a function can be better, at least you know that the value isn't changed somewhere unexpected.
- TameAntelope 5y agoI just avoid most of it by moving the complex conditional logic into a separate component and rendering that. I don't think I've run into many issues with this strategy, and keeps my return statements nice and clean.
- matt-attack 5y agoI find ternaries alone to be totally adequate.
- TameAntelope 5y agoOh they work, but stacking them tends to make future reading of the code harder due to their increased complexity.
- kingdomcome50 5y agoNested ternaries can by structured to look a look like `if/else` if you put the `?/:` in the right spots. It's a balance. Extracting logic into new components (and often new files!) isn't exactly ergonomic either. I've found it's better to decrease readability if it makes understanding the logic more straight-forward (i.e. not hunting through several files to exhaust all possible outputs).
- vlunkr 5y agoAgreed, if it's more complex than a single ternary, or && statements, make a new component.
- jeffalbertson 5y agoits rare but im ok with a nested ternary when its being passed down as a prop. dont wanna use a function in that situation. sidenote - I discovered vladimirs blog a while back and love it. high quality content
- phailhaus 5y agoWhen logic gets more complicated than a single ternary (e.g., handling pending/error/result state), I've found that a `useMemo` gives me the flexibility I need. It also keeps the messy logic out of the final JSX, which tends to make it read better.
- 9wzYQbTYsAIc 5y agoThe React maintainers did put a lot of effort into making hooks. The variety of hooks seem to cover all the use cases for programming.
- heyparkerj 5y agoI've personally rarely even needed to useMemo, I just write a function that returns the JSX I need and { myFunction() } in the functional component's return (given React).
- phailhaus 5y agoIf you're using Typescript that can get a bit annoying, since you'll also need to pass in whatever props necessary for the function to handle the logic + type them. Makes it nice and easy to test in isolation though.
- heyparkerj 5y agoTypically the function has access to whatever props or state it needs because it's in the scope of the component. It's not pure, but it's rare that I need to verbosely pass props into the function call and then accept them in myFunction. I don't typically test these functions in isolation as the component itself is pure and I test the output of the component as a whole instead.
- phailhaus 5y agoOh gotcha, I thought this was a function that was defined outside of the component.
- Eric_WVGG 5y agoDrives me nuts that there's no straightforward way to do a switch statement in JSX. <Layout> <Header /> {switch (page) { case 'home': return <Home /> case 'about': return <About /> default: return <NotFound /> }} <Footer /> </Layout>
- 9wzYQbTYsAIc 5y agoThe example you put forward could be resolved through using a router. The router implementations are essentially fancy switches without ”switch”.
- Eric_WVGG 5y agoyeah, I use reach/router for this kind of thing. A page switcher was just the first example that popped into my head. Here's a real world example if it matters… const Grid = (gridItems) => ( <Grid> {gridItems.map(item => ( <GridItem {...item} key={item._key} /> _} </Grid> )} const GridItem = (props) => { switch(props._type) { case 'image': return <GridImage {...props} /> case 'video': return <GridVideo {...props} /> case 'copy': return <GridCopy {...props} /> // slideshows, 3D stuff, newsletter signup forms… default: return <></> } } … and I suppose a case could even be made for the granularity of this superfluous <GridItem /> component… but regardless this is just a minor annoyance that comes up from time to time.
- methyl 5y agoI'd write it like this const gridComponent = { image: GridImage, video: GridVideo, copy: GridCopy } const Grid = (gridItems) => ( <Grid> {gridItems.map(item => { const GridComponent = gridComponent[item._type] return GridComponent ? <GridComponent {...item} key={item._key} /> : null }} </Grid> )}
- 9wzYQbTYsAIc 5y agoThat’s definitely representative of the compactness that is possible with modern JSX.
- the_gipsy 5y agoNice article. To me it boils down to one JS problem: Lacking `if` expression, and one React problem: Side effects of un/mounting.
- ryanto 5y agoGreat article! I will say I've found that using prettier makes nested ternaries much more readable. I couldn't imagine using them without prettier, but since every app I work on these days uses prettier nested ternaries aren't so bad.
- horsawlarway 5y agoMost of these are just "tips on using conditionals" and have very little to do with JSX itself.
- sfvisser 5y agoTrue. Also many of these are not actual issues and are just behavior inherent to using JavaScript. If you don’t like your conditional logic in your template just give them a name and explicit boolean type and be done with it. Still a useful overview of common idioms though for people who just get started.
- horsawlarway 5y ago> Still a useful overview of common idioms though for people who just get started. I agree - I just think labeling them as JSX issues is pretty disingenuous. JSX is literally just running them as standard js expressions here - which I actually find delightful: they work the same as they do in normal javascript (quirks included :D ). The author seems to want something like ng-if="condition", and I find DSLs in this space a real abomination.
- laurent123456 5y agoI think a better approach is to move the logic to a renderXxx() function: function renderInput(props:Props) { // Early exit if props are not as expected if (!props.cond1) return null; return <JSX/>; } Then the parent markup is much cleaner, without any conditional: {renderInput(props)}
- hombre_fatal 5y agoThough you're also adding indirection. Not always worth it when you're just trying to do some conditional logic next to the code/components that it's related to. Ideally we have the tools to decide when to add indirection ourselves instead being forced to do it to deal with complexity. You could also see this in callback-hell when we'd flatten callback trees with indirection—the tree looked flatter in the editor but we just moved code around. async/await gave us the tools to decide when we actually wanted it.
- eyelidlessness 5y agoHow is that indirection? These are semantically identical: {renderIf(foo)} <RenderIf foo={foo} /> The latter is a bit more verbose, sure, but it’s both more idiomatic and a better optimization target.
- dimgl 5y agoThis pattern is fine, but I would make this a component. function ConditionalComponent(props: Props) { if (!props.condition) return null return <UnderlyingComponent {...props} /> }
- tobr 5y agoHard disagree on nested ternaries, or rather chained ternaries, which are just as easy to read and reason about as chained if else statements: if (isA) { return A; } else if (isB) { return B; } else if (isC) { return C; } else { return D; } is equivalent to return isA ? A : isB ? B : isC ? C : D;
- dimgl 5y agoSure, when you're neatly using booleans with names that are three characters long, this is super easy to parse. Now throw in long functions with multiple arguments, and all kinds of different access patterns and this ternary starts to look like a nightmare.
- akvadrako 5y agoIt's still often the cleanest way and I find myself doing something like this: let x = somelongfunc(arg, blah(etc)) ? value1 : some other condition ? value2 : fallback What I usually want is pattern matching expressions, but those are not in many languages.
- brimble 5y agoTry as I might, I have to reason through ternaries every time I encounter them. I think the = being so removed from what it's actually assigning, but without grouping parens, is what messes me up. Plus I always forget what the punctuation characters mean—if/elseif/else uses words, so I don't have to remember.
- tobr 5y agoJust read whatever has a question mark after it with the intonation of a question, and it flows naturally.
- eyelidlessness 5y ago> I think the = being so removed from what it's actually assigning, but without grouping parens, is what messes me up. I almost always use grouping parentheses for this reason, unless it’s a very short single line expression. That said, if/else if/else has a different colocation problem: it puts the assignment further from the initial declaration, making its scope less obvious (unless you’re hoisting var, which is awful for its own reasons).
- latchkey 5y agoThe more conditional logic you have in a component, the harder it is to write tests for.
- eyelidlessness 5y agoAgreed. This is also true for functions and especially true for stateful classes/objects.
- maxwellg 5y agoThere's a babel plugin for adding an Angular-style "display-if" to JSX - https://www.npmjs.com/package/babel-plugin-jsx-display-if https://www.npmjs.com/package/babel-plugin-jsx-display-if I've only seen it used in projects once or twice but it really does wonders for readability once the team becomes OK with the new-ish syntax
- beaconstudios 5y agoprotip: you can use an IIFE (immediately invoked function expression) to put whatever conditionals or logic you want inside JSX! I use this all the time and it's much nicer for more complex blocks: {(() => { switch(state) { case 'loading': return <Loading />; case 'ready': return <Component />; case 'error': return <Error error={error} />; } })()}
- deleted 5y ago[deleted]
- deleted 5y ago[deleted]
- bacro 5y agoWaste of memory by creating new function objects on each render.
- symlinkk 5y agoYeah probably best to move this to a function outside the render loop with useCallback
- beaconstudios 5y agoMicro-optimisations aren't useful. If you get to the point where the performance cost of inline functions is a bottleneck, you probably shouldn't be using React at all.
- bacro 5y agoMaybe, but I just prefer to have a named function defined with a useCallback and just call it.
- beaconstudios 5y agoThat's fine, but that's more of a stylistic preference than a performance concern.
- sabertoothed 5y agoI missed a v-if in react/JSX. My code is so hard to read in JSX.
- eyelidlessness 5y agoThis is usually a good indicator that your components may be doing too much. If your component’s JSX specifically is hard to read, you probably have multiple components implemented within one.
- LAC-Tech 5y agoIMHO this is an anti-pattern. This involves people writing giant blocks of JSX that are hard to read. Especially with && all over the place. Just split up your component into smaller components with max one conditional each. Related, can anyone tell me the origins of the bizarre react practice of one file per component? I'm assume some mega corp told people it was "best practice", but the end result is way too many files with hard to read components.
- conradludgate 5y agoI'm assuming it dates back to class components and Class per File ideologies
- LAC-Tech 5y agoInteresting! And yeah Class per file had the exact same problem - classes were big and did too much. And now everyone hates "OO"
- esperent 5y agoThe ideas described here are just tools and the article contains plenty of simple and readable examples of the tools being used well. If you end up using them to create giant unreadable blocks of JSX then you're using the tools wrong, but that doesn't make the tools themselves an anti-pattern. The anti-pattern there is "giant unreadable blocks of code".