Have you ever built a React component with a render function that has a ton of messy ternary statements that are used for determining what to render according to your component state? Don’t worry, it happens to the best of us!

In this post I’ll teach you a simple trick you can use in your stateful React components to clean up complicated rendering logic. I call this technique “Sub Rendering”. It’s derived from the process of dividing large complex functions into smaller simple sub functions. Just like when you divide functions into sub functions, you can also divide render functions into sub renders methods.

When would you want to do this?

Imagine we had a React component that made a use of an external API to get a list of results. This component will start in a loading state upon mounting before it initiates an API call. When called, it should return a response with results. But this API is pretty unstable so you also want to display something to handle an error if it occurs. There’s also a chance that there are no results. In that case the response will be an empty array.

This component will have three states. These states are isLoading , results and hasError . The component will also make use of four child components that are defined somewhere else (out of the scope for this post). These child components are <Loading /> , <Results /> , <NoResults /> and <Error /> . None of these children components require any props except for the <Results /> component. This just takes a results prop which will be the response payload stored in the results state of our main component.

Hopefully this makes sense. Let’s talk about the different states and how they should affect what gets rendered in the end.

isLoading

isLoading is a boolean value which we will initiate as true because when the component first mounts we haven’t fetched the results yet. So it remains true until either results are returned (full or empty) or an error occurs with the API call. When this is true we’ll render the <Loading /> component.

results

Assuming the API call worked without any problems, we should receive a list of results. Since it’s a list, we will initialize it in the state as an empty array ( [] ). We do this because we expect the API response to always be an array. In the case of no results, we’ll still have an empty array in our response payload. Provided the endpoint is not still in an isLoading: true state, we will render the results in the <Results /> component. If the response payload is an empty array then we’ll render the <NoResults /> component instead.

hasError

We have the hasError boolean for in case an error occurs during the API call. This is initialized as false in the component state and only set to true if the API call fails. If the hasError is true we’ll render the <Error /> component.

Render function in action

Okay, so we’ve spoke enough about what we’re going to do. Now let’s actually do it!

In this example I’ll leave out some code like the imports and the API logic that handles changing the component state. Since the focus here is to explain the rendering logic.

The most common way of implementing this render logic is to implement it using nested ternary statement like this:

class MyComponent extends Component {

state = {

hasError: false,

isLoading: true,

results: [],

} componentDidMount() {

// implement API call logic here

} render() {

const { hasError, isLoading, results } = this.state; return (

<Container>

{isLoading ? <Loading /> :

<>

{hasError ? <Error /> :

<>

{results.length === 0 ? <NoResults /> :

<Results results={results} />

}

</>

}

</>

}

</Container>

);

}

}

First of all, if you’re not familiar with the <>...</> syntax. That is the shorthand syntax for React Fragments and you can read all about them in a blog post that I wrote over here. Also I sneaked in a <Container> component to act as a styling wrapper to make our component a little bit more complicated for demonstration purposes.

I’m not sure about you but I find this kind of logic hard to read at first glance. It makes perfect sense while you’re writing it but looking back on it 2 days later might cause you to question your sanity. If you don’t get it, let me write the algorithm in a small snippet to help you grasp it.

if (isLoading) {

return <Loading />;

} if (hasError) {

return <Error />;

} if (results.length === 0) {

return <NoResults />;

}

return <Results results={results} />;

Now remember, when the compiler encounters a return statement, it leaves the function. That’s why I’m not writing else if statements. This is actually an amazing solution that we could have used to replace our complicated nested ternary logic used above.

But our nested ternary logic above is wrapped in a <Container /> component so we can’t do this. Now what do we do?

There are two possible options here.

Option 1: Move all the logic out of the wrapper to it’s own component

Moving the logic in the wrapper out to it’s own component is a pretty straight forward task. The only annoying thing is that you’d have to pass all your state props down to the component. But even doing that isn’t much effort thanks to our wonderful ES6 ability which allows us to spread props. In the end, doing this would leave you with the following simple render function:

render() {

return (

<Container>

<ComponentRenderer {...this.state} />

</Container>

);

}

And then you’d have your <ComponentRenderer> Stateless Functional Component written like this:

const ComponentRenderer = ({ hasError, isLoading, results }) => {

if (isLoading) {

return <Loading />;

} if (hasError) {

return <Error />;

} if (results.length === 0) {

return <NoResults />;

}

return <Results results={results} />;

}

This is a really neat solution. I’d recommend it over our initial solution because it’s much easier to read logically from a programmers point of view. It moves the logic of what to render out of the JSX which is certainly preferred over keeping it all inside the JSX.

But there’s another solution that doesn’t entail creating a whole new component.

Option 2: Implement Sub Rendering

I did start this post off by talking about sub rendering before I rambled on with that option 1 tangent. So let’s get back to it! I’ll show you the completed example first since it pretty much speaks for itself:

class MyComponent extends Component {

state = {

hasError: false,

isLoading: true,

results: [],

} componentDidMount() {

// implement API call logic here

} renderResults() {

const { hasError, isLoading, results } = this.state; if (isLoading) return <Loading />;

if (hasError) return <Error />;

if (results.length < 1) return <NoResults />;

return <Results results={results} />;

} render() {

return (

<Container>

{this.renderResults()}

</Container>

)

}

}

Told you it speaks for itself. If you understand your JavaScript and React fundamentals then this should make sense.

For fun I changed the results.length === 0 to results.length < 1 . Essentially, they do the same thing but I’m sure there’s some ridiculous tiny performance boost to doing one over the other. Heck now I’m inspired to research that and write about it. Alright I’m getting carried away again.

The great thing about this solution is that — just like option 1 — it’s very clean. You don’t need to create a whole new component. The best part, you don’t need to pass any arguments around! The renderResults event handler already has access to the component’s state via this . If you’ve read my post about binding this, this is a great example of an event handler that doesn’t need to be bound to this .

Option 3: Handle rendering logic inside each individual sub component

Shout out to Julius Koronci who commented on my original post to point out this technique. Thanks for helping me expand on this post!

Another way of simplifying render logic is to move that logic down to the context of where it needs to be rendered. Like this:

class MyComponent extends Component {

state = {

hasError: false,

isLoading: true,

results: [],

}



componentDidMount() {

// implement API call logic here

} render() {

const { hasError, isLoading, results } = this.state; return (

<Container>

<Loading isLoading={isLoading} />

<Error hasError={hasError} />

<Results {...this.state} />

</Container>

)

}

}

In this case we “render” each sub component. Well not really. What we’re doing here is we are moving the rendering logic down to the sub component level. For example, rendering the <Loading> sub component would work by sending the isLoading prop down to it. Inside the <Loading> sub component we'll use the isLoading prop to determine what to render. If isLoading is true , return the loading component content. If isLoading is false , return null i.e. render nothing.

That loading component would be implemented as a Stateless Functional Component like this:

const Loading = ({ isLoading }) => {

if (isLoading) {

return <LoadingContent />;

} return null;

}

Now yes, you could turn this into a ternary statement to make it all fit on one or two lines. But it’s important to maintain the readability of your code to save time for yourself and other developers who come across this function later.

Another note regarding this is that there are some variations on how the <Results /> component could be implemented. I had to pass all the state values through to it because it still needs to check whether or not to render based on the isLoading and hasError states. If isLoading or hasError was true, null would be returned.

Then you still need to move the logic to render no results down into that component. This ends up taking us back to the problem we had in the first place. Or you could repeat yourself and leave <NoResults /> in the main component and also render it based on isLoading , hasError and the length of results .

Nevertheless, this is still a viable solution to clean up your main component. But in my opinion. It’s more of a move your mess solution than a clean up your mess solution. It could affect the re-usability of your components and is basically option 1’s solution split up further.

Conclusion

Given the title of this post you’ve probably guesses that I opt for the Sub Rendering approach discussed in option 2. I prefer it because it keeps things simple with everything easy to read in one place. The rendering logic is scoped to the main component’s state. Since the state dictates what should get rendered, it makes sense to keep the rendering logic at the same level. I’m a huge fan of splitting components up into smaller components. But only if it makes things easier to read when looking at the big picture.

Well, that’s all I really need to say about Sub Rendering in React components. Once you see it in practice it’s pretty self explanatory. I hope you’ve learnt something new from this post. If you’ve enjoyed it then be sure to like it, clap for it and share it!

This post was inspired by Code Complete: A Practical Handbook of Software Construction, Second Edition. This book always challenges me to be better. And being better is great, so check it out if you haven’t yet!

You can follow me on Facebook, Twitter, Medium, YouTube or even connect with me on LinkedIn to keep updated with new content that I post.

Finally, let me know what you prefer to do when dealing with rendering logic in the comments below. I haven’t really figured out what the best practice way of doing things here are. But I do believe that whatever path you choose, it should be consistent!