When we are developing a React application, we often need to show or hide an element given a certain condition. Be it a user interaction, the presence of data coming from a request, or even permission levels. This is called conditional rendering, and we’ll look at different approaches to handling those cases. IF IF is the most basic approach of all and probably the one you will mostly see, but it is restricted to the total block of the component. You use an IF with your condition and return the element to be rendered. Observe the example below: UserList = { (isLoading) { } ( <li>{user.name}</li> ) }; const ( ) => { isLoading, results } if return Loading... < > span </ > span return {result.map((user) => ( < > ul ))} </ > ul Above we have a list of users, who receives the isLoading props and results. If isLoading is true, we return a message stating that it is loading, otherwise, we render the list of users. Note that we use since there is no need as we use the within the first . Do not be afraid to use more than one per function, this practice will help you to reduce the complexity of the code. DO NOT ELSE return IF return There is nothing wrong with the above example, on the contrary, the code is simple and readable. However, as I mentioned, this approach is restricted to rendering a whole block and in many cases, we can not use it. Imagine that our List of users is more complex, that it has a title and a button to create a new user. In this case, the page should always show the title and button. Loading message would only appear in the place of the list, as shown below: Considering the above image, we can not use the because we would need to repeat an entire block of code just to change the contents of the list. IF For these cases, we can write an expression using the && logical operator within our , as in the example below: JSX UserList = ( <div> <h1>Users</h1> <a href="/users/create">New User</a> </div> <div> {isLoading && <span>Loading...</span>} {!isLoading && ( <ul> {result.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> )} </div> ); const ( ) => { isLoading, results } < > div </ > div As in the example above, we can use braces ( ) to insert a expression inside . React will get the result of each expression and will render on the screen. When an expression returns a , or , just ignores, not rendering anything! {} JS JSX Boolean undefined null React React Native will NOT ignore undefined and you end up with a run time error. Rather to convert the value to boolean in that case if you are using React outside of web environment. Understanding expressions When we use the logical operator JS will interpret each value of the expression until it reaches the last value or some of the evaluation end up in a . In any case, the last value interpreted will be the result of the expression. Example: && falsy value user = { : , : , : , }; userName = user && user.name address = user && user.address zipCode = user && user.address && user.address.zipcode const name 'John' surname 'Doe' address null const // John const // null const // null As in the example above, from line 7, the first element, , is interpreted. Since it is a , the cursor passes to the second element, which is . Since this is the last interpreted element, the result of the expression will be the value of the second element: ' '. user truthy value user.name John In the case of the , the first element, , is interpreted and identified as and then the cursor passes to the second element , which in this case has the value of . Since we have no more elements and the last value interpreted was , this becomes the result of the expression. address user truthy user.address null null Finally, in the case of , the same process as the occurs. It interprets the first, then it passes to the second. However, since the second element is , the cursor finishes the execution of the expression and, as always, assumes the last interpreted value, in this case: . zipCode address falsy null It is important to understand how the expressions work, because sometimes you may be expecting the result to be false, and then you get null or 0. Readability Since we are including logic within , we have to be careful to maintain the readability of the code. For this reason, for expressions with more than two items, it is interesting to create a variable to abstract the validation items. An example: JSX {!isLoading && !results.length && ( )} shouldDisplayNotFound = !isLoading && !results.length; {shouldDisplayNotFound && ( )} NO RESULTS FOUND < > span </ > span // VS const NO RESULTS FOUND < > span </ > span The above example serves only to understand the concept, I know that we do not see clearly the advantages of one approach to another. That’s because we have a small block of code in front of us. But below we will see this same applied concept in a more practical way. Let’s say for our list of users, we need to show a message when no record has been found. However, we can only display this message after the result has already been loaded. In this way, we can check the number of results through . Below, we will see how this logic would look without using variables. results.length UserList = ( <div> <h1>Users</h1> <a href="/users/create">New User</a> </div> <div> {isLoading && <span>Loading...</span>} {!isLoading && !results.length && ( <span>No Results Found</span> )} {!isLoading && results.length > 0 && ( <ul> {result.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> )} </div> ); const ( ) => { isLoading, results } < > div </ > div Above we see that the logic begins to get a bit complex to be in the middle of , but we still can understand it. Basically, in all checks, we need to consider , to ensure that the list is loaded and only then base us on to tell whether there are results or not. JSX isLoading results.length Note that to show the list, we checked whether the length was greater than 0. Remember how the values of an expression are interpreted and how the result of the expression is obtained? If we just checked if the length was truthy, writing , if it were , that would be the result of the expression and then React would print on the screen. results.length && 0 0 Considering the previous example, imagine now that besides treating a state where there are no results, you also need to display a different message if there is an error in the request. Notice what the code looks like: UserList = ( <div> <h1>Users</h1> <a href="/users/create">New User</a> </div> <div> {error && <span>Something is not right!</span>} {!error && isLoading && <span>Loading...</span>} {!error && !isLoading && !results.length && ( <span>No Results Found</span> )} {!error && !isLoading && results.length > 0 && ( <ul> {result.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> )} {!error && !isLoading && results.length > 0 && ( <ul> {result.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> )} </div> ); const ( ) => { isLoading, results, error } < > div </ > div Notice that the complexity begins to get bigger and it becomes difficult to understand what is happening. Let’s explore ways to simplify our code! Transforming Validations into Variables As mentioned above, it is possible to take the piece from each expression that refers to the validation and extract them to variables, with declarative names. In this way, our logic will become a bit clearer. Note the code below: UserList = { shouldDisplayLoader = !error && isLoading; shouldDisplayNoResults = !error && !isLoading && !results.length; shouldDisplayList = !error && !isLoading && results.length > ; ( <div> <h1>Users</h1> <a href="/users/create">New User</a> </div> <div> {error && <span>Something is not right!</span>} {shouldDisplayLoader && <span>Loading...</span>} {shouldDisplayNoResults && ( <span>No Results Found</span> )} {shouldDisplayList && ( <ul> {result.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> )} </div> ); } const ( ) => { isLoading, results, error } const const const 0 return < > div </ > div We can see that extracting the logic from the middle of , it is possible to understand it more easily, besides the variable name already make clear what the expression represents. Anyone who would maintain the code in the future could change the validations without much fear. JSX Extraction of blocks Note that in our example, most validations are centered on a single block. So, we can extract the block in question into a function, or turn it into another component, as in the example below: UserListResults = { (error) { ; } (isLoading) { ; } (!results.length) { ; } ( <li key={user.id}>{user.name}</li> ); } UserList = ( <h1>Users</h1> <a href="/users/create">New User</a> <div> ); const ( ) => { error, results, isLoading } if return Something is not right! < > span </ > span if return Loading... < > span </ > span if return No Results Found < > span </ > span return {result.map((user) => ( < > ul ))} </ > ul const ( ) => { isLoading, results, error } < > div </ > div < = = = /> UserListResults error {error} results {results} isLoading {isLoading} </ > div Observe how all our logic got simpler by extracting the block to another component. See also that we are using again the approach of the with the return. This approach is also known as Early return. When we use , our logics tend to be simpler, since the next validation does not have to worry about the previous one. Ex: To check if it is loading, I do not have to worry if an error has occurred or not, since this case has already been handled in the previous . IFs Early return IF Use of ternary Ternaries are also welcome in cases where two blocks alternate given a certain condition. Note the mockup below a form for user creation: As the image above, we have the fields: Name, Email, Country and a place reserved for what would be the Province field. The Province field, requires you to select a country first. Once the country is selected, the status field will be available on the screen, according to the code below: UserForm = ( <h1>User</h1> <div> <div className="row"> <div className="form-group"> <label>Name</label> <input type="text" name="name" value={values.name} onChange={onChange} /> </div> <div className="form-group"> <label>Email</label> <input type="email" name="email" value={values.email} onChange={onChange} /> </div> </div> <div className="row"> <div className="form-group"> <label>Country</label> <CountrySelector name="country" value={values.country} onChange={onChange} /> </div> <div className="form-group"> <label>Province</label> {values.country ? <ProvinceSelector name="province" value={values.province} onChange={onChange} /> : <span>Select a country first</span> } </div> </div> </div> </div> ); const ( ) => { onChange, values } < > div Let’s not go into the or into the logic to save the state, just assume that will always have the updated value of the form fields. onChange values Based on values, we write a ternary to display the if the value is present, otherwise, we will display the message. The ternary is very useful, but it is not applicable everywhere. I would avoid using it to render large blocks of code, by making it difficult to read in some cases. ProvinceSelector country Using Handlers Handlers are also used in specific cases. When you need to render different content to the same block, all of them based on a given value. Imagine that you are developing a generic component for presenting data. Each data has a type, which can be , , , etc … For each type, you need to designate a different formatting/style. For this situation, we could use handlers. date number currency handlers = { : <NumberDisplay>{value}< > time: <TimeDisplay time={value} customProps /> date: <DateDisplay date={value} showTime={ } /> : value, }; displayData = { handler = handlers[type] || handlers.default; handler(value); }; DataDisplay = ( ); const number => value /NumberDisplay> currency: value => <CurrencyDisplay customProps value={value} / => value => value false default => value const ( ) => type, value const return const ( ) => { type, value } {displayData(type, value)} < > div </ > div Handlers are nothing more than a key-value object, where the is the unique identifier of each handler within the context and the is a function that will be responsible for rendering the particular block. key value In the example above, each handler receives the argument, but you could receive as many arguments as you need since the handlers execution logic is implemented by you. value Note that in the function, we get a and a as arguments. in line 10, we assign one of the handlers to the handler, based on the or assign the default handler, using a evaluation. displayData type value const type short-circuit Short-circuit evaluations tend to perform better than a switch case, since it evaluates only the existence of the value. In line 11, we execute the handler passing the value and we return the result. Finally, we execute the function inside our component, passing the and . displayData DataDisplay type value Using other approaches to solve this kind of problem, your code could turn into chaos. In contrast, you might notice that using handlers, the example above would easily scale, rendering 10, 20, or even 30 different data types based on the type, if necessary. Conclusion All of the above approaches are valid when used in the right context, it is up to us to use the super power called “Good Sense” to use each one in the appropriate situation. And you, do you use any specific approach you would like to share with us? Did you like the post? Help us spread the word by giving a like and sharing on social networks so that more people have access! ❤️ ️ Don’t forget to follow me, to be notified about future posts! ✌