Component Anti-Patterns #498
calebjacob
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Our team has been building BOS components for almost a year now and we've come across a few anti-patterns that can lead to poor UX and/or performance issues. Grab a cup of coffee (or your alternative beverage of choice) and let's dive in!
Cascading HTTP Requests
One of the biggest performance bottlenecks for websites is the number of HTTP requests required to load the page. It's important to focus on keeping the number of requests as low as possible whenever implementing new features.
Here are two components that can quickly cause a large number of cascading requests:
AccountProfile
TimeAgo
This following example would trigger 2 additional HTTP requests for every post rendered:
If we look at these two components, we'll discover the following:
AccountProfile
has to callSocial.get(
${props.accountId}/profile/**, "final")
to render the account's avatar and display nameTimeAgo
has to call an endpoint to calculate a timestamp based on the passedprop.blockHeight
So what's the solution here? In most examples like this, the best solution is to use Query API to fetch all of the required data in a single request. We can then pass this data down to our components through props. If we look at
AccountProfile
andTimeAgo
, we'll notice we can pass inprofile
andblockTimestamp
props to avoid these extra requests:If our example was rendering 50 posts, we'd now be making a single request instead of 101 (+1 for the initial fetch, +50 for each
AccountProfile
, and +50 for eachTimeAgo
)! Although 101 is a reasonable number of Dalmatians, it's an outrageous number of HTTP requests for a single feature.Re-rendering Expensive Components
When developing more complex features, we can end up with large components that have many state updates and HTTP requests. Every time a piece of state is updated, a re-render will occur for the entire component (which can also re-trigger the HTTP requests depending on our logic). Due to the complexities of how the VM parses and runs JavaScript in a secure environment, these re-renders can be more expensive than you would expect compared to working within a typical React environment.
The best solution for avoiding expensive re-renders is breaking up large components with multiple features into separate components that implement each feature separately.
Here's an example of a component that could be refactored:
In the example above, the entire component and
ReallyComplexComponent
will have to re-render wheneversetData()
is called. IfReallyComplexComponent
doesn't need access todata
then we can refactor to reduce the impact of callingsetData()
:Then
DataComponent
would become:This allows
DataComponent
to re-render as much as it needs to without impactingReallyComplexComponent
.We can also utilize hooks like
useMemo()
anduseCallback()
in special scenarios as needed - though they should be used sparingly. Before reaching for those hooks, we should first see if we can simply restructure our existing component logic. Hooks likeuseMemo()
anduseCallback()
can be helpful to reduce re-renders, but they also introduce a layer of complexity and potential for bugs. There's always a tradeoff. For more information on these two hooks, please give this a read: https://kentcdodds.com/blog/usememo-and-usecallbackLinks Triggering Full Page Refresh
Our component gateway is rendered by a Next JS application. Next provides a Link component that implements an
<a>
tag. When clicked, it performs client side routing to avoid a full page refresh. ThisLink
component is provided globally by the VM via customElements.Whenever we need to render an anchor that links inside of our application, we should always use the
<Link>
component instead of rendering a standard<a>
tag:When you need to apply styles to the link, you can pass a
className
or use Styled Components:The exception to this rule is when you need to link to an external website. In those cases, you can simply use a regular
<a>
tag since a full page refresh is necessary anyways:Wrap Up
Please feel free to ask questions, make suggestions, and add your own thoughts on anti-patterns we should watch out for. Thanks for reading!
Beta Was this translation helpful? Give feedback.
All reactions