{"id":8128,"date":"2017-06-20T11:16:59","date_gmt":"2017-06-20T08:16:59","guid":{"rendered":"http:\/\/railsware.com\/blog\/?p=8128"},"modified":"2021-08-12T16:58:06","modified_gmt":"2021-08-12T13:58:06","slug":"hideable-react-component-using-hoc","status":"publish","type":"post","link":"https:\/\/railsware.com\/blog\/hideable-react-component-using-hoc\/","title":{"rendered":"Hideable React component using HOC"},"content":{"rendered":"\n<p>Imagine that we have a very simple <code>React<\/code> component <code>Hello<\/code> which greets a user with a message:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:javascript\">import React from 'react';\nimport PropTypes from 'prop-types';\n\nexport default function Hello({ name }) {\n  return (<\/pre>\n\n\n\n<p><strong>Hello, {name}!<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:javascript\">  );\n}\nHello.propTypes = {\n  name: PropTypes.string.isRequired\n};\n<\/pre>\n\n\n\n<p>And here is how we use it:<\/p>\n\n\n\n<div>&nbsp;<\/div>\n\n\n\n<pre class=\"wp-block-preformatted\">&nbsp;<\/pre>\n\n\n\n<p>Great simple component, we really like the code. But it turns out that the <code>name<\/code> value can be <code>null<\/code> until we fetch it from a Back-End. We don&#8217;t want our users to see a stub: <span class=\"lang:javascript crayon-inline\">Hello, !<\/span>. So we decided to hide the text if the <code>name<\/code> is <code>null<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:javascript\">import React from 'react';\nimport PropTypes from 'prop-types';\n\nexport default function Hello({ name }) {\n  if (name == null) {\n    return null;\n  }\n  return (<\/pre>\n\n\n\n<p><strong>Hello, {name}!<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:javascript\">  );\n}\nHello.propTypes = {\n  name: PropTypes.string.isRequired\n};\n<\/pre>\n\n\n\n<p>Looks good! Stub has gone and the message shows up only when the profile is loaded. But there is another issue &#8211; some noisy error message in the console:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:2 wrap:true nums:false\">Warning: Failed prop type: Required prop 'name' was not specified in 'Hello'\n<\/pre>\n\n\n\n<p>Ouch, because <code>name<\/code> is <code>null<\/code> it doesn&#8217;t pass <code>propTypes<\/code> validation. Maybe we need to provide some default value like empty string and check for the name not to be empty?<\/p>\n\n\n\n<p>This escalates quickly and we are now shedding crutches instead of focusing on the business logic of the component. What if we have dozens of required properties &#8211; name, age and other data which will come later. It doesn&#8217;t sound cool if we have to set up some default values for all of them.<\/p>\n\n\n\n<p>Of course, this case can be handled directly by JavaScript outside of the component:<\/p>\n\n\n\n<div>{user.name &amp;&amp; }<\/div>\n\n\n\n<pre class=\"wp-block-preformatted\">&nbsp;<\/pre>\n\n\n\n<p>This solution is okay but what if we use <code>Redux<\/code> and provide <code>name<\/code> value directly to container:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:javascript\">import { connect } from 'react-redux';\nimport Hello from '..\/..\/components\/Hello';\n\nconst mapStateToProps = (state) =&gt; {\n  const user = state.user;\n  return {\n    name: user.name\n  };\n};\n\nexport default connect(mapStateToProps)(Hello);\n<\/pre>\n\n\n\n<p>Now we need to pass the <code>user<\/code> object to the Hello&#8217;s parent for using JavaScript-hiding. Or we should rollback to the uglier solution and handle empty state within <code>Hello<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Solution<\/h3>\n\n\n\n<p>But can we do better? Can we have a simple, original, neat version of <code>Hello<\/code> with a normal <code>propTypes<\/code> validation and make them hideable on some condition? As a bonus, we want to extract such behavior and reuse with other components.<\/p>\n\n\n\n<p>It&#8217;s actually doable! Because React components are composable, we can easily create Higher Order Component(HOC) which accepts original always-shown component and turns it into hideable when some condition is met. Here is the code of such <code>HOC<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:javascript\">import React from 'react';\nimport PropTypes from 'prop-types';\n\nconst propTypes = {\n  hideComponent: PropTypes.bool,\n  children: PropTypes.node\n};\nconst defaultProps = {\n  hideComponent: false\n};\n\nfunction getDisplayName(component) {\n  return component.displayName || component.name || 'Component';\n}\n\nexport default function Hideable(component) {\n  function HideableComponent({ hideComponent, children, ...props }) {\n    if (hideComponent) {\n      return null;\n    }\n    return React.createElement(component, props, children);\n  }\n  HideableComponent.displayName = `Hideable(${getDisplayName(component)})`;\n  HideableComponent.propTypes = propTypes;\n  HideableComponent.defaultProps = defaultProps;\n  return HideableComponent;\n}\n<\/pre>\n\n\n\n<p>So <code>Hideable<\/code> is a simple function which wraps any passed-in component into a proxy <code>HideableComponent<\/code>. This proxy component has only one real property &#8211; <code>hideComponent<\/code> and if the value of <code>hideComponent<\/code> is truthy &#8211; child component will not be rendered:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2017\/06\/Hideable-e1497452196938.png\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"321\" src=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2017\/06\/Hideable-e1497452196938.png\" alt=\"Hideable component\" class=\"wp-image-8740\"\/><\/a><\/figure>\n\n\n\n<p>Let&#8217;s make our <code>Hello<\/code> hideable and wrap it into <code>Redux<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:javascript\">import { connect } from 'react-redux';\nimport Hello from '..\/..\/components\/Hello';\nimport Hideable from '..\/..\/utils\/hideable';\n\nconst HideableHello = Hideable(Hello);\n\nconst mapStateToProps = (state) =&gt; {\n  const user = state.user;\n  if (!user.name) {\n    return { hideComponent: true };\n  }\n  return {\n    name: user.name\n  };\n};\nexport default connect(mapStateToProps)(HideableHello);\n<\/pre>\n\n\n\n<p>Now <code>mapStateToProps<\/code> manages visibility of the <code>Hello<\/code> component and we haven&#8217;t changed anything within the original <code>Hello<\/code> component.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p>So we started from a very simple component and ended up with the same component topped-up with hideable logic. Using the same HOC approach, you can build more complex shareable behaviors and use them to build complex components from the simple ones.<\/p>\n\n\n\n<p>That&#8217;s it! I hope this neat trick will help you write simpler React components and reduce an amount of a boilerplate code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Imagine that we have a very simple React component Hello which greets a user with a message: import React from &#8216;react&#8217;; import PropTypes from &#8216;prop-types&#8217;; export default function Hello({ name }) { return ( Hello, {name}! ); } Hello.propTypes = { name: PropTypes.string.isRequired }; And here is how we use it: &nbsp; &nbsp; Great simple&#8230;<\/p>\n","protected":false},"author":25,"featured_media":9409,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[],"coauthors":["Sergii Boiko"],"class_list":["post-8128","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"aioseo_notices":[],"categories_data":[{"name":"Engineering","link":"https:\/\/railsware.com\/blog?category=development"}],"post_thumbnails":"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2017\/06\/Hideable-e1497452196938.png","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8128","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/comments?post=8128"}],"version-history":[{"count":33,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8128\/revisions"}],"predecessor-version":[{"id":13989,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8128\/revisions\/13989"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media\/9409"}],"wp:attachment":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media?parent=8128"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/categories?post=8128"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/tags?post=8128"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/coauthors?post=8128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}