Posted on Leave a comment

Intro to React Hooks

Hooks make it possible to organize logic in components, making them tiny and reusable without writing a class. In a sense, they’re React’s way of leaning into functions because, before them, we’d have to write them in a component and, while components have proven to be powerful and functional in and of themselves, they have to render something on the front end. That’s all fine and dandy to some extent, but the result is a DOM that is littered with divs that make it gnarly to dig through through DevTools and debug.

Well, React Hooks change that. Instead of relying on the top-down flow of components or abstracting components in various ways, like higher-order components, we can call and manage flow inside of a component. Dan Abramov explains it well in his Making Sense of React post:

Hooks apply the React philosophy (explicit data flow and composition) inside a component, rather than just between the components. That’s why I feel that Hooks are a natural fit for the React component model.

Unlike patterns like render props or higher-order components, Hooks don’t introduce unnecessary nesting into your component tree. They also don’t suffer from the drawbacks of mixins.

The rest of Dan’s post provides a lot of useful context for why the React team is moving in this direction (they’re now available in React v16.7.0-alpha) and the various problems that hooks are designed to solve. The React docs have an introduction to hooks that, in turn, contains a section on what motivated the team to make them. We’re more concerned with how the heck to use them, so let’s move on to some examples!

The important thing to note as we get started is that there are nine hooks currently available, but we’re going to look at what the React docs call the three basic ones: useState(), useEffect, and setContext(). We’ll dig into each one in this post with a summary of the advanced hooks at the end.

Defining state with useState()

If you’ve worked with React at any level, then you’re probably familiar with how state is generally defined: write a class and use this.state to initialize a class:

class SomeComponent extends React.component {   constructor(props)   super(props);   this.state = {     name: Barney Stinson // Some property with the default state value       } }

React hooks allow us to scrap all that class stuff and put the useState() hook to use instead. Something like this:

import { useState } from 'react';      function SomeComponent() {   const [name, setName] = useState('Barney Stinson'); // Defines state variable (name) and call (setName) -- both of which can be named anything }

Say what?! That’s it! Notice that we’re working outside of a class. Hooks don’t work inside of a class because they’re used in place of them. We’re using the hook directly in the component:

import { useState } from 'react';      function SomeComponent() {   const [name, setName] = useState('Barney Stinson');      return     <div>       <p>Howdy, {name}</p>     </div> }

Oh, you want to update the state of name? Let’s add an input and submit button to the output and call setName to update the default name on submission.

import { useState } from 'react'      function SomeComponent() {   const [input, setValue] = useState("");   const [name, setName] = useState('Barney Stinson');      handleInput = (event) => {     setValue(event.target.value);   }      updateName = (event) => {     event.preventDefault();     setName(input);     setValue("");   }      return (     <div>       <p>Hello, {name}!</p>       <div>         <input type="text" value={input} onChange={handleInput} />         <button onClick={updateName}>Save</button>       </div>     </div>   ) }

See the Pen React Hook: setState Example by Geoff Graham (@geoffgraham) on CodePen.

Notice something else in this example? We’re constructing two different states (input and name). That’s because the useState() hook allows managing multiple states in the same component! In this case, input is the property and setValue holds the state of the input element, which is called by the handleInput function then triggers the updateName function that takes the input value and sets it as the new name state.

Create side effects with useEffect()

So, defining and setting states is all fine and dandy, but there’s another hook called useEffect() that can be used to—you guessed it—define and reuse effects directly in a component without the need for a class or the need to use both redundant code for each lifecycle of a method (i.e. componentDidMount, componentDidUpdate, and componentWillUnmount).

When we talk about effects, we’re referring to things like API calls, updates to the DOM, and event listeners, among other things. The React documentation cites examples like data fetching, setting up subscriptions, and changing the DOM as possible use cases for this hook. Perhaps the biggest differentiator from setState() is that useEffect() runs after render. Think of it like giving React an instruction to hold onto the function that passes and then make adjustments to the DOM after the render has happened plus any updates after that. Again, the React documentation spells it out nicely:

By default, it runs both after the first render and after every update. […] Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. React guarantees the DOM has been updated by the time it runs the effects.

Right on, so how do we run these effects? Well, we start off by importing the hook the way we did for setState().

import { useEffect } from 'react';

In fact, we can call both setState() and useEffect() in the same import:

import { useState, useEffect } from 'react';

Or, construct them:

const { useState, useEffect } = React;

So, let’s deviate from our previous name example by hooking into an external API that contains user data using axios inside the useEffect() hook then renders that data into a list of of users.

First, let’s bring in our hooks and initialize the App.

const { useState, useEffect } = React  const App = () => {   // Hooks and render UI }

Now, let’s put setState() to define users as a variable that contains a state of setUsers that we’ll pass the user data to once it has been fetched so that it’s ready for render.

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);   // Our effects come next }

Here’s where useEffect() comes into play. We’re going to use it to connect to an API and fetch data from it, then map that data to variables we can call on render.

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);      useEffect(() => {     // Connect to the Random User API using axios     axios("https://randomuser.me/api/?results=10")       // Once we get a response, fetch name, username, email and image data       // and map them to defined variables we can use later.       .then(response =>         response.data.results.map(user => ({           name: `{user.name.first} $  {user.name.last}`,           username: `{user.login.username}`,           email: `{user.email}`,           image: `{user.picture.thumbnail}`         }))       )       // Finally, update the `setUsers` state with the fetched data       // so it stores it for use on render       .then(data => {         setUsers(data);       });   }, []);      // The UI to render }

OK, now let’s render our component!

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);    useEffect(() => {     axios("https://randomuser.me/api/?results=10")       .then(response =>         response.data.results.map(user => ({           name: `{user.name.first} $  {user.name.last}`,           username: `{user.login.username}`,           email: `{user.email}`,           image: `{user.picture.thumbnail}`         }))       )       .then(data => {         setUsers(data);       });   }, []);      return (     <div className="users">       {users.map(user => (         <div key={user.username} className="users__user">           <img src={user.image} className="users__avatar" />           <div className="users__meta">             <h1>{user.name}</h1>             <p>{user.email}</p>           </div>         </div>       ))}     </div>   ) }

Here’s what that gets us:

See the Pen React Hook: setEffect example by Geoff Graham (@geoffgraham) on CodePen.

It’s worth noting that useEffect() is capable of so, so, so much more, like chaining effects and triggering them on condition. Plus, there are cases where we need to cleanup after an effect has run—like subscribing to an external resource—to prevent memory leaks. Totally worth running through the detailed explanation of effects with cleanup in the React documentation.

Context and useContext()

Context in React makes it possible to pass props down from a parent component to a child component. This saves you from the hassle of prop drilling. However, you could only make use of context in class components, but now you can make use of context in functional components using useContext() . Let’s create a counter example, we will pass the state and functions which will be used to increase or decrease the count from the parent component to child component using useContext(). First, let’s create our context:

const CountContext = React.createContext();

We’ll declare the count state and increase/decrease methods of our counter in our App component and set up the wrapper that will hold the component. We’ll put the context hook to use in the actual counter component in just a bit.

const App = () => {   // Use `setState()` to define a count variable and its state   const [count, setCount] = useState(0);      // Construct a method that increases the current `setCount` variable state by 1 with each click   const increase = () => {     setCount(count + 1);   };      // Construct a method that decreases the current `setCount` variable state by 1 with each click.   const decrease = () => {     setCount(count - 1);   };    // Create a wrapper for the counter component that contains the provider that will supply the context value.   return (     <div>       <CountContext.Provider         // The value is takes the count value and updates when either the increase or decrease methods are triggered.         value={{ count, increase, decrease }}       >         // Call the Counter component we will create next         <Counter />       </CountContext.Provider>     </div>   ); };

Alright, onto the Counter component! useContext() accepts an object (we’re passing in the CountContext provider) and allows us to tell React exactly what value we want (`count) and what methods trigger updated values (increase and decrease). Then, of course, we’ll round things out by rendering the component, which is called by the App.

const Counter = () => {   const { count, increase, decrease } = useContext(CountContext);   return (     <div className="counter">       <button onClick={decrease}>-</button>       <span className="count">{count}</span>       <button onClick={increase}>+</button>     </div>   ); };

And voilà! Behold our mighty counter with the count powered by context objects and values.

See the Pen React hooks – useContext by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Wrapping up

We’ve merely scratched the surface of what React hooks are capable of doing, but hopefully this gives you a solid foundation. For example, there are even more advanced hooks that are available in addition to the basic ones we covered in this post. Here’s a list of those hooks with the descriptions offered by the documentation so you can level up now that you’re equipped with the basics:

Hook Description
userReducer() An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method.
useCallback() Returns a memoized callback. Pass an inline callback and an array of inputs. useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.
useMemo() Returns a memoized value. Pass a “create” function and an array of inputs. useMemo will only recompute the memoized value when one of the inputs has changed.
useRef() useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
useImperativeMethods useImperativeMethods customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeMethods should be used with forwardRef.
useLayoutEffect The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

The post Intro to React Hooks appeared first on CSS-Tricks.

CSS-Tricks

Posted on Leave a comment

Using React Portals to Render Children Outside the DOM Hierarchy

Say we need to render a child element into a React application. Easy right? That child is mounted to the nearest DOM element and rendered inside of it as a result.

render() {   return (     <div>       // Child to render inside of the div     </div>   ); }

But! What if we want to render that child outside of the div somewhere else? That could be tricky because it breaks the convention that a component needs to render as a new element and follow a parent-child hierarchy. The parent wants to go where its child goes.

That’s where React Portals come in. They provide a way to render elements outside the DOM hierarchy so that elements are a little more portable. It may not be a perfect analogy, but Portals are sort of like the pipes in Mario Bros. that transport you from the normal flow of the game and into a different region.

The cool thing about Portals? Even though they trigger their own events that are independent of the child’s parent element, the parent is still listening to those events, which can be useful for passing events across an app.

We’re going to create a Portal together in this post then make it into a re-usable component. Let’s go!

The example we’re building

Here’s a relatively simple example of a Portal in action:

See the Pen React Portal by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Toggling an element’s visibility is nothing new. But, if you look at the code carefully, you’ll notice that the outputted element is controlled by the button even though it is not a direct descendent of it. In fact, if you compare the source code to the rendered output in DevTools, you’ll see the relationship:

So the outputted element’s parent actually listens for the button click event and allows the child to be inserted even though it and the button are separate siblings in the DOM. Let’s break down the steps for creating this toggled Portal element to see how it all works.

Step 1: Create the Portal element

The first line of a React application will tell you that an App element is rendered on the document root using ReactDOM. Like this;

ReactDOM.render(<App />, document.getElementById("root"));

We need to place the App element in an HTML file to execute it:

<div id="App"></div>

Same sort of thing with Portals. First thing to creating a Portal is to create a new div element in the HTML file.

<div id="portal"></div>

This div will serve as our target. We’re using #portal as the ID, but it doesn’t have to be that. Any component that gets rendered inside this target div will maintain React’s context. We need to store the div as the value of a variable so we can make use of the Portal component that we’ll create:

const portalRoot = document.getElementById("portal");

Looks a lot like the method to execute the App element, right?

Step 2: Create a Portal component

Next, let’s set up the Portal as a component:

class Portal extends React.Component {   constructor() {     super();     // 1: Create a new div that wraps the component     this.el = document.createElement("div");   }   // 2: Append the element to the DOM when it mounts   componentDidMount = () => {     portalRoot.appendChild(this.el);   };   // 3: Remove the element when it unmounts   componentWillUnmount = () => {     portalRoot.removeChild(this.el);   };   render() {     // 4: Render the element's children in a Portal     const { children } = this.props;     return ReactDOM.createPortal(children, this.el);   } }

Let’s step back and take a look at what is happening here.

We create a new div element in the constructor and set it as a value to this.el. When the Portal component mounts, this.el is appended as a child to that div in the HTML file where we added it. That’s the <div id="portal"></div> line in our case.

The DOM tree will look like this.

<div> // Portal, which is also portalRoot   <div> // this.el   </div> </div>

If you’re new to React and are confused by the concept of mounting and unmounting an element, Jake Trent has a good explanation. TL;DR: Mounting is the moment the element is inserted into the DOM.

When the component unmounts we want to remove the child to avoid any memory leakage. We will import this Portal component into another component where it gets used, which is the the div that contains the header and button in our example. In doing so, we’ll pass the children elements of the Portal component along with it. This is why we have this.props.children.

Step 3: Using the Portal

To render the Portal component’s children, we make use of ReactDOM.createPortal(). This is a special ReactDOM method that accepts the children and the element we created. To see how the Portal works, let’s make use of it in our App component.

But, before we do that, let’s cover the basics of how we want the App to function. When the App loads, we want to display a text and a button — we can then toggle the button to either show or hide the Portal component.

class App extends React.Component {   // The initial toggle state is false so the Portal element is out of view   state = {     on: false   };    toggle = () => {     // Create a new "on" state to mount the Portal component via the button     this.setState({       on: !this.state.on     });   };   // Now, let's render the components   render() {     const { on } = this.state;     return (       // The div where that uses the Portal component child       <div>         <header>           <h1>Welcome to React</h1>         </header>         <React.Fragment>           // The button that toggles the Portal component state           // The Portal parent is listening for the event           <button onClick={this.toggle}>Toggle Portal</button>           // Mount or unmount the Portal on button click           <Portal>             {               on ?                 <h1>This is a portal!</h1>               : null             }           </Portal>         </React.Fragment>       </div>     );   } }

Since we want to toggle the Portal on and off, we need to make use of component state to manage the toggling. That’s basically a method to set a state of on to either true or false on the click event. The portal gets rendered when on is true; else we render nothing.

This is how the DOM looks like when the on state is set to true.

When on is false, the Portal component is not being rendered in the root, so the DOM looks like this.

More use cases

Modals are a perfect candidate for Portals. In fact, the React docs use it as the primary example for how Portals work:

See the Pen Example: Portals by Dan Abramov (@gaearon) on CodePen.

It’s the same concept, where a Portal component is created and a state is used to append the its child elements to the Modal component.

We can even insert data from an outside source into a modal. In this example, the App component lists users fetched from an API using axios.

See the Pen React Portal 3 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

How about tooltips? David Gilberston has a nice demo:

See the Pen React Portal Tooptip by David Gilbertson (@davidgilbertson) on CodePen.

J Scott Smith shows how Portals can be used to escape positioning:

He has another slick example that demonstrates inserting elements and managing state:

Summary

That’s a wrap! Hopefully this gives you a solid base understanding of Portals as far as what they are, what they do, and how to use them in a React application. The concept may seem trivial, but having the ability to move elements outside of the DOM hierarchy is a handy way to make components a little more extensible and re-usable… all of which points to the core benefits of using React in the first place.

More information

The post Using React Portals to Render Children Outside the DOM Hierarchy appeared first on CSS-Tricks.

CSS-Tricks

Posted on Leave a comment

Animating Between Views in React

You know how some sites and web apps have that neat native feel when transitioning between two pages or views? Sarah Drasner has shown some good examples and even a Vue library to boot.

These animations are the type of features that can turn a good user experience into a great one. But to achieve this in a React stack, it is necessary to couple crucial parts in your application: the routing logic and the animation tooling.

Let’s start with animations. We’ll be building with React, and there are great options out there for us to leverage. Notably, the react-transition-group is the official package that handles elements entering and leaving the DOM. Let’s explore some relatively straightforward patterns we can apply, even to existing components.

Transitions using react-transition-group

First, let’s get familiar with the react-transition-group library to examine how we can use it for elements entering and leaving the DOM.

Single components transitions

As a simple example of a use case, we can try to animate a modal or dialog — you know, the type of element that benefits from animations that allow it enter and leave smoothly.

A dialog component might look something like this:

import React from "react";  class Dialog extends React.Component {   render() {     const { isOpen, onClose, message } = this.props;     return (       isOpen && (         <div className="dialog--overlay" onClick={onClose}>           <div className="dialog">{message}</div>         </div>       )     );   } }

Notice we are using the isOpen prop to determine whether the component is rendered or not. Thanks to the simplicity of the recently modified API provided by react-transition-group module, we can add a CSS-based transition to this component without much overhead.

First thing we need is to wrap the entire component in another TransitionGroup component. Inside, we keep the prop to mount or unmount the dialog, which we are wrapping in a CSSTransition.

import React from "react"; import { TransitionGroup, CSSTransition } from "react-transition-group";  class Dialog extends React.Component {   render() {     const { isOpen, onClose, message } = this.props;     return (       <TransitionGroup component={null}>         {isOpen && (           <CSSTransition classNames="dialog" timeout={300}>             <div className="dialog--overlay" onClick={onClose}>               <div className="dialog">{message}</div>             </div>           </CSSTransition>         )}       </TransitionGroup>     );   } }

Every time isOpen is modified, a sequence of class names changes will happen in the dialog’s root element.

If we set the classNames prop to "fade", then fade-enter will be added immediately before the element mounts and then fade-enter-active when the transition kicks off. We should see fade-enter-done when the transition finishes, based on the timeout that was set. Exactly the same will happen with the exit class name group at the time the element is about to unmount.

This way, we can simply define a set of CSS rules to declare our transitions.

.dialog-enter {   opacity: 0.01;   transform: scale(1.1); }  .dialog-enter-active {   opacity: 1;   transform: scale(1);   transition: all 300ms; }  .dialog-exit {   opacity: 1;   transform: scale(1); }  .dialog-exit-active {   opacity: 0.01;   transform: scale(1.1);   transition: all 300ms; }

JavaScript Transitions

If we want to orchestrate more complex animations using a JavaScript library, then we can use the Transition component instead.

This component doesn’t do anything for us like the CSSTransition did, but it does expose hooks on each transition cycle. We can pass methods to each hook to run calculations and animations.

<TransitionGroup component={null}>   {isOpen && (     <Transition       onEnter={node => animateOnEnter(node)}       onExit={node => animateOnExit(node)}       timeout={300}     >       <div className="dialog--overlay" onClick={onClose}>         <div className="dialog">{message}</div>       </div>     </Transition>   )} </TransitionGroup>

Each hook passes the node to the callback as a first argument — this gives control for any mutation we want when the element mounts or unmounts.

Routing

The React ecosystem offers plenty of router options. I’m gonna use react-router-dom since it’s the most popular choice and because most React developers are familiar with the syntax.

Let’s start with a basic route definition:

import React, { Component } from 'react' import { BrowserRouter, Switch, Route } from 'react-router-dom' import Home from '../views/Home' import Author from '../views/Author' import About from '../views/About' import Nav from '../components/Nav'  class App extends Component {   render() {     return (       <BrowserRouter>         <div className="app">           <Switch>             <Route exact path="/" component={Home}/>             <Route path="/author" component={Author} />             <Route path="/about" component={About} />           </Switch>         </div>       </BrowserRouter>     )   } }

We want three routes in this application: home, author and about.

The BrowserRouter component handles the browser’s history updates, while Switch decides which Route element to render depending on the path prop. Here’s that without any transitions:

Don’t worry, we’ll be adding in page transitions as we go.

Oil and water

While both react-transition-group and react-router-dom are great and handy packages for their intended uses, mixing them together can break their functionality.

For example, the Switch component in react-router-dom expects direct Route children and the TransitionGroup components in react-transition-group expect CSSTransition or Transition components to be direct children of it too. So, we’re unable to wrap them the way we did earlier.

We also cannot toggle views with the same boolean approach as before since it’s handled internally by the react-router-dom logic.

React keys to the rescue

Although the solution might not be as clean as our previous examples, it is possible to use the libraries together. The first thing we need to do is to move our routes declaration to a render prop.

<BrowserRouter>   <div className="app">     <Route render={(location) => {       return (         <Switch location={location}>           <Route exact path="/" component={Home}/>           <Route path="/author" component={Author} />           <Route path="/about" component={About} />         </Switch>       )}     /> </BrowserRouter>

Nothing has changed as far as functionality. The difference is that we are now in control of what gets rendered every time the location in the browser changes.

Also, react-router-dom provides a unique key in the location object every time this happens.

In case you are not familiar with them, React keys identify elements in the virtual DOM tree. Most times, we don’t need to indicate them since React will detect which part of the DOM should change and then patch it.

<Route render={({ location }) => {   const { pathname, key } = location    return (     <TransitionGroup component={null}>       <Transition         key={key}         appear={true}         onEnter={(node, appears) => play(pathname, node, appears)}         timeout={{enter: 750, exit: 0}}       >         <Switch location={location}>           <Route exact path="/" component={Home}/>           <Route path="/author" component={Author} />           <Route path="/about" component={About} />         </Switch>       </Transition>     </TransitionGroup>   ) }}/>

Constantly changing the key of an element — even when its children or props haven’t been modified — will force React to remove it from the DOM and remount it. This helps us emulate the boolean toggle approach we had before and it’s important for us here because we can place a single Transition element and reuse it for all of our view transitions, allowing us to mix routing and transition components.

Inside the animation function

Once the transition hooks are called on each location change, we can run a method and use any animation library to build more complex scenes for our transitions.

export const play = (pathname, node, appears) => {   const delay = appears ? 0 : 0.5   let timeline    if (pathname === '/')     timeline = getHomeTimeline(node, delay)   else     timeline = getDefaultTimeline(node, delay)    timeline.play() }

Our play function will build a GreenSock timeline here depending on the pathname, and we can set as many transitions as we want for each different routes.

Once the timeline is built for the current pathname, we play it.

const getHomeTimeline = (node, delay) => {   const timeline = new Timeline({ paused: true });   const texts = node.querySelectorAll('h1 > div');    timeline     .from(node, 0, { display: 'none', autoAlpha: 0, delay })     .staggerFrom(texts, 0.375, { autoAlpha: 0, x: -25, ease: Power1.easeOut }, 0.125);    return timeline }

Each timeline method digs into the DOM nodes of the view and animates them. You can use other animation libraries instead of GreenSock, but the important detail is that we build the timeline beforehand so that our main play method can decide which one should run for each route.

Success!

I’ve used this approach on lots of projects, and though it doesn’t present obvious performance issues for inner navigations, I did notice a concurrency issue between the browser’s initial DOM tree build and the first route animation. This caused a visual lag on the animation for the first load of the application.

To make sure animations are smooth in each stage of the application, there’s one last thing we can do.

Profiling the initial load

Here’s what I found when auditing the application in Chrome DevTools after a hard refresh:

You can see two lines: one blue and one red. Blue represents the load event and red the DOMContentLoaded. Both intersect the execution of the initial animations.

These lines are indicating that elements are animating while the browser hasn’t yet finished building the entire DOM tree or it’s parsing resources. Animations account for big performance hits. If we want anything else to happen, we’d have to wait for the browser to be ready with these heavy and important tasks before running our transitions.

After trying a lot of different approaches, the solution that actually worked was to move the animation after these events — simple as that. The issue is that we can’t rely on event listeners.

window.addEventListener(‘DOMContentLoaded’, () => {   timeline.play() })

If for some reason, the event occurs before we declare the listener, the callback we pass will never run and this could lead to our animations never happening and an empty view.

Since this is a concurrency and asynchronous issue, I decided to rely on promises, but then the question became: how can promises and event listeners be used together?

By creating a promise that gets resolved when the event takes place. That’s how.

window.loadPromise = new Promise(resolve => {   window.addEventListener(‘DOMContentLoaded’, resolve) })

We can put this in the document head or just before the script tag that loads the application bundle. This will make sure the event never happens before the Promise is created.

Plus, doing this allows us to use the globally exposed loadPromise to any animation in our application. Let’s say that we don’t only want to animate the entry view but a cookie banner or the header of the application. We can simply call each of these animations after the promise has resolved using then along with our transitions.

window.loadPromise.then(() => timeline.play())

This approach is reusable across the entire codebase, eliminating the issue that would result when an event gets resolved before the animations run. It will defer them until the browser DOMContentLoaded event has passed.

See now that the animation is not kicking off until the red line appears.

The difference is not only on the profiling report — it actually solves an issue we had in a real project.

Wrapping up

In order to act as reminders, I created a list of tips for me that you might find useful as you dig into view transitions in a project:

  • When an animation is happening nothing else should be happening. Run animations after all resources, fetching and business logic have completed.
  • No animation is better than crappy animations If you can’t achieve a good animation, then removing it is a fair sacrifice. The content is more important and showing it is the priority until a good animation solution is in place.
  • Test on slower and older devices. They will make it easier for you to catch spots with weak performance.
  • Profile and base your improvements in metrics. Instead of guessing as you go, like I did, see if you can spot where frames are being dropped or if something looks off and attack that issue first.

That’s it! Best of luck with animating view transitions. Please post a comment if this sparked any questions or if you have used transitions in your app that you’d like to share!

The post Animating Between Views in React appeared first on CSS-Tricks.

CSS-Tricks

Posted on Leave a comment

Making SVG icon libraries for React apps

Nicolas Gallagher:

At Twitter I used the approach described here to publish the company’s SVG icon library in several different formats: optimized SVGs, plain JavaScript modules, React DOM components, and React Native components.

There is no One True Way© to make an SVG icon system. The only thing that SVG icon systems have in common is that, somehow, some way, SVG is used to show that icon. I gotta find some time to write up a post that goes into all the possibilities there.

One thing different systems tend to share is some kind of build process to turn a folder full of SVG files into a more programmatically digestible format. For example, gulp-svg-sprite takes your folder of SVGs and creates a SVG sprite (chunk of <symbol>s) to use in that type of SVG icon system. Grunticon processes your folder of SVGs into a CSS file, and is capable of enhancing them into inline SVG. Gallagher’s script creates React components out of them, and like he said, that’s great for delivery to different targets as well as performance optimization, like code splitting.

This speaks to the versatility of SVG. It’s just markup, so it’s easy to work with.

Direct Link to ArticlePermalink

The post Making SVG icon libraries for React apps appeared first on CSS-Tricks.

CSS-Tricks

Posted on Leave a comment

Rendering Lists Using React Virtualized

Working with data in React is relatively easy because React is designed to handle data as state. The hassle begins when the amount of data you need to consume becomes massive. For example, say you have to handle a dataset which is between 500-1,000 records. This can result in massive loads and lead performance problems. Well, we’re going to look at how we can make use of virtualized lists in React to seamlessly render a long list of data in your application.

We’re going to use the React Virtualized component to get what we need. It will allow us to take large sets of data, process them on the fly, and render them with little-to-no jank.

The setup

React Virtualized already has a detailed set of instructions to get it up and running, so please check out the repo to get started.

We’re going to want data to work with, so we will set up a function which uses faker to create a large data set.

function createRecord(count) {   let records = [];    for (let i = 0; i < count; i++) {     records.push({       username: faker.internet.userName(),       email: faker.internet.email()     });   }   return records; }

Next, we will pass it the number of data records we want to create, like so:

const records = createRecord(1000);

Alright, now we have what we need to work on rendering a list of those records!

Creating a virtualized list

Here’s the list we want to create, sans styling. We could make use of the few presentational styles that the library includes by importing the included CSS file, but we’re going to leave that out in this post.

See the Pen React Virtualized 1 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Go ahead and re-run that demo. Crazy fast, right?

You might wonder what the heck React Virtualized is doing behind the scenes to make that happen. Turns out it’s a bunch of crazy and cool sizing, positioning, transforms and transitions that allow the records to scroll in and out of view. The data is already there and rendered. React Virtualized creates a window frame that allows records to slide in and out of view as the user scrolls through it.

To render a virtualized list in React Virtualized, we make use of its List component, which uses a Grid component internally to render the list.

First, we start by setting up rowRenderer, which is responsible for displaying a single row and sets up an index that assigns an ID to each record.

rowRenderer = ({ index, isScrolling, key, style }) => {     return (       <div key={key} style={style}>         <div>{this.props.data[index].username}</div>         <div>{this.props.data[index].email}</div>       </div>     );   };

As you can see, this returns a single div node that contains two additional divs: one for the username and another for the email. You know, a common list pattern to display users.

rowRenderer accepts several parameters. Here’s what they are and what each one does:

  • index: The numeric ID of a record.
  • isScrolling: Indicates if the scrolling is occurring in the List component.
  • isVisible: Determines if a row is visible or out of view.
  • key: The records position in the array.
  • parent: Defines whether the list is a parent or a child of another list.
  • style: A style object to position the row.

Now that we know more about the rowRenderer function, let’s make put it to use in the List component:

<List   rowCount={this.props.data.length}   width={width}   height={height}   rowHeight={rowHeight}   rowRenderer={this.rowRenderer}   overscanRowCount={3} />

You may have noticed a few new parameters. Here’s what they are:

  • rowCount: This takes the numbers of a row in a list that we pass to calculate the length of our list.
  • width: The width of the list.
  • height: The height of the list.
  • rowHeight: This can be a number or a function that returns a row height given its index.
  • rowRenderer: This is responsible for rendering the row. the list is not supposed to be passed directly, so we pass the rowRenderer function that we created in this tutorial.
  • overscanRowCount: This is used to render additional rows in the direction the user scrolls. It reduces the chances of the user scrolling faster than the virtualized content is rendered.

At the end, your code should look something like this;

const { List } = ReactVirtualized  ...  const height = 700; const rowHeight = 40; const width = 800;  class App extends React.Component {   rowRenderer = ({ index, isScrolling, key, style }) => {     return (       <div key={key} style={style}>         <div>{this.props.data[index].username}</div>         <div>{this.props.data[index].email}</div>       </div>     );   };    render() {     return (       <div>         <h2>Details</h2>         <List           rowCount={this.props.data.length}           width={width}           height={height}           rowHeight={rowHeight}           rowRenderer={this.rowRenderer}           overscanRowCount={3}         />       </div>     );   } }

Cell measurer

According to the documentation, a cell measurer is a higher-order component that is used to temporarily render a list. It’s not yet visible to the user at this point, but the data is held and ready to display.

Why should you care about this? The popular use case is a situation where the value of your rowHeight is dynamic. React Virtualized can render the height of the row on render then cache that height so it no longer needs to calculate as data scrolls out of view — it’s always the right height, no matter the content it contains!

First, we create our cache, which can be done in our component’s constructor using CellMeasurerCache:

constructor() {   super()   this.cache = new CellMeasurerCache({     fixedWidth: true,     defaultHeight: 100   }) }

We make use of the cache when we set up the List component;

<List   rowCount={this.props.data.length}   width={rowWidth}   height={listHeight}   deferredMeasurementCache={this.cache}   rowHeight={this.cache.rowHeight}   rowRenderer={this.renderRow}   overscanRowCount={3} />

The value passed to deferredMeasurementCache will be used to temporarily rendering the data, then — as the calculated value for rowHeight comes in — additional rows will flow in like they were always there.

Next, though, we will make use of React Virtualized’s CellMeasurer component inside our rowRenderer function instead of the div we initially set up as a placeholder:

rowRenderer = ({ index, parent, key, style }) => {   return (     <CellMeasurer       key={key}       cache={this.cache}       parent={parent}       columnIndex={0}       rowIndex={index}     >       <div style={style}>         <div>{this.props.data[index].username}</div>         <div>{this.props.data[index].email}</div>       </div>     </CellMeasurer>   );   };

Now the data is fetched, cached and ready to display in the virtual window at will!

Virtualized table

Yeah, so the main point of this post is to cover lists, but what if we actually want to render data to a table instead? React Virtualized has you covered on that front, too. In this case, we will make use of Table and Column components that come baked into React Virtualized.

Here’s how we would put those components to use in our primary App component:

class App extends React.Component {   render() {     return (       <div>         <h2>Details</h2>         <Table           width={500}           height={300}           headerHeight={20}           rowHeight={40}           rowCount={this.props.data.length}           rowGetter={({ index }) => this.props.data[index]}         >           <Column             label='Username'             dataKey='username'             width={100}           />                        <Column             width={200}             label='Email'             dataKey='email'           />         </Table>       </div>     );   } }

The Table component accepts the following parameters:

  • width: The width of the table.
  • height: The height of the table.
  • headerHeight: The table header height.
  • rowHeight: The height of a row given its index.
  • rowCount: This is the initial number of rows we want in the table. It’s the same as the way we defined the number of records we wanted to start with in the List component example.
  • rowGetter: This returns the data of a specific row by its index.

If you take a look at the Column component, you will notice that we put a dataKey parameter to use. That passes the data for each column we called in the dataKey, which receives a unique identifier for that data. Remember that in the function where we create our random data, we make use of two keys; username and email. This is why we have the dataKey of one column set as username and the other set as email.

In conclusion

Hopefully, this walkthrough gives you a good idea of what React Virtualized is capable of doing, how it can make rendering large data sets into lists and tables super fast, and how to put it to use in a project.

We’ve only scratched the surface here. The library is capable of handling a lot of other use cases, like generating placeholders for the data records on scroll, an infinite loading component to fetch and cache data in real-time, a method for allowing arrow keys to navigate through the data, and a slick grid and masonry layouts that we didn’t even cover here.

That should give you a lot to play around with!

Plus, the package is highly maintained. In fact, you can join the Slack group to keep up with the project, contribute to it, and generally get to connect with other folks.

It’s also worth noting that React Virtualized has it own tag in StackOverflow and that can be a good resource to find questions other people have asked about it, or even post your own questions.

Oh, and if you’ve put React Virtualized to use on a project, we’d love to know it! Share it with us in the comments with some notes on how you approached it or what you learned from it.

The post Rendering Lists Using React Virtualized appeared first on CSS-Tricks.

CSS-Tricks

Posted on Leave a comment

Compound Components in React Using the Context API

Compound components in React allow you to create components with some form of connected state that’s managed amongst themselves. A good example is the Form component in Semantic UI React.

To see how we can implement compound components in a real-life React application, we’ll build a compound (multi-part) form for login and sign up. The state will be saved in the form component and we’ll put React’s Context AP to use to pass that state and the method from the Context Provider to the component that needs them. The component that needs them? It will become a subscriber to Context Consumers.

Here’s what we’re building:

See the Pen React Compound Component by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Here’s a rough outline that shows how the following steps fit together:

Form is the provider with state, Form Panel is the consumer receiving state, Panel displays the panel based on the state, and Signup and Login render the form views in the Panel.

Before treading any further, you may want to brush up on the React Context API if you haven’t already. Neal Fennimore demonstrates the concept in this post and my primer on it is worth checking out as well.

Step 1: Creating context

First, let’s initialize a new context using the React Context API.

const FormContext = React.createContext({}); const FormProvider = FormContext.Provider; const FormConsumer = FormContext.Consumer;

The provider, FormProvider, will hold the application state, making it available to components that subscribe to FormConsumer.

Step 2: Implement provider

One panel contains the form to log in and the other contains the form to sign up. In the provider, we want to declare the state, which determines the active panel, i.e. the form currently in display. We’ll also create a method to switch from one panel to another when a heading is clicked.

class Form extends React.Component {   state = {     activePanel: "login"   };    render() {     return (       <React.Fragment>         <FormProvider           value={{             activePanel: this.state.activePanel,             actions: {               handlePanelSwitch: newPanel => {                 this.setState({                   activePanel: newPanel                 });               }             }           }}         >           {this.props.children}         </FormProvider>       </React.Fragment>     );   } }

By default, the login panel will be shown to the user. When the signup panel is clicked, we want to make it the active panel by setting the state of activePanel to signup using the method handlePanelSwitch().

Step 3: Implement Consumers

We’ll use FormConsumer to make context available to the components that subscribe to it. That means the FormPanel component that handles displaying panels will look like this:

const FormPanel = props => {   return (     <FormConsumer>       {({ activePanel }) =>         activePanel === props.isActive ? props.children : null       }     </FormConsumer>   ); };

And the Panel component will look like this:

const Panel = props => (   <FormConsumer>     {({ actions }) => {       return (         <div onClick={() => actions.switchPanel(props.id)}>           {props.children}         </div>       );     }}   </FormConsumer> );

To understand what is happening, let’s understand the approach here. The login and signup panels will have unique IDs that get passed via props to the Panel component. When a panel is selected, we get the ID and and use it to set activePanel to swap forms. The FormPanel component also receives the name of the panel via the isActive prop and we then check to see if the returned value is true. If it is, then the panel is rendered!

To get the full context, here is how the App component looks:

const App = () => {   return (     <div className="form-wrap">       <Form>         <div className="tabs">           <Panel id="login">             <h2 className="login-tab">Login</h2>           </Panel>           <Panel id="signup">             <h2 className="signup-tab">Sign Up</h2>           </Panel>         </div>          <FormPanel isActive="login">           <Login />         </FormPanel>          <FormPanel isActive="signup">           <SignUp />         </FormPanel>       </Form>     </div>   ); };

You can see how the components are composed when activePanel matches isActive (which is supposed to return true). The component is rendered under those conditions.

With that done, the Login component looks like this:

const Login = () => {   return (     <React.Fragment>       <div id="login-tab-content">         <form className="login-form" action="" method="post">           <input             type="text"             className="input"             id="user_login"             placeholder="Email or Username"           />           <input             type="password"             className="input"             id="user_pass"             placeholder="Password"           />           <input type="checkbox" className="checkbox" id="remember_me" />           <label htmlFor="remember_me">Remember me</label>            <input type="submit" className="button" value="Login" />         </form>       </div>     </React.Fragment>   ); };

And the SignUp component:

const SignUp = () => {   return (     <React.Fragment>       <div id="signup-tab-content" className="active tabs-content">         <form className="signup-form" action="" method="post">           <input             type="email"             className="input"             id="user_email"             placeholder="Email"           />           <input             type="text"             className="input"             id="user_name"             placeholder="Username"           />           <input             type="password"             className="input"             id="user_pass"             placeholder="Password"           />           <input type="submit" className="button" value="Sign Up" />         </form>       </div>     </React.Fragment>   ); };

Get it? Got it? Good!

You can use this pattern anytime you have components in your React application that need to share implicit state. You can also build compound components using React.cloneElement().

References

The post Compound Components in React Using the Context API appeared first on CSS-Tricks.

CSS-Tricks

Posted on Leave a comment

An Overview of Render Props in React

An Overview of Render Props in React

Using render props in React is a technique for efficiently re-using code. According to the React documentation, “a component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.” To understand what that means, let’s take a look at the render props pattern and then apply it to a couple of light examples.

The render props pattern

In working with render props, you pass a render function to a component that, in turn, returns a React element. This render function is defined by another component, and the receiving component shares what is passed through the render function.

This is what this looks like:

class BaseComponent extends Component {   render() {     return <Fragment>{this.props.render()}</Fragment>;   } }

Imagine, if you will, that our App is a gift box where App itself is the bow on top. If the box is the component we are creating and we open it, we’ll expose the props, states, functions and methods needed to make the component work once it’s called by render().

The render function of a component normally has all the JSX and such that form the DOM for that component. Instead, this component has a render function, this.props.render(), that will display a component that gets passed in via props.

Example: Creating a counter

See the Pen React Render Props by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Let’s make a simple counter example that increases and decreases a value depending on the button that is clicked.

First, we start by creating a component that will be used to wrap the initial state, methods and rendering. Creatively, we’ll call this Wrapper:

class Wrapper extends Component {   state = {     count: 0   };    // Increase count   increment = () => {     const { count } = this.state;     return this.setState({ count: count + 1 });   };    // Decrease count   decrement = () => {     const { count } = this.state;     return this.setState({ count: count - 1 });   };    render() {     const { count } = this.state;      return (       <div>         {this.props.render({           increment: this.increment,           decrement: this.decrement,           count: count         })}       </div>     );   } }

In the Wrapper component, we specify the methods and state what gets exposed to the wrapped component. For this example, we need the increment and decrement methods. We have our default count set as 0. The logic is to either increment or decrement count depending on the method that is triggered, starting with a zero value.

If you take a look at the return() method, you’ll see that we are making use of this.props.render(). It is through this function that we pass methods and state from the Wrapper component so that the component that is being wrapped by it will make use of it.

To use it for our App component, the component will look like this:

class App extends React.Component {   render() {     return (       <Wrapper         render={({ increment, decrement, count }) => (           <div>             <div>               <h3>Render Props Counter</h3>             </div>             <div>               <p>{count}</p>               <button onClick={() => increment()}>Increment</button>               <button onClick={() => decrement()}>Decrement</button>             </div>           </div>         )}       />     );   } }

Example: Creating a data list

The gain lies in the reusable power of render props, let’s create a component that can be used to handle a list of data which is obtainable from an API.

See the Pen React Render Props 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

What do we want from the wrapper component this time? We want to pass the source link for the data we want to render to it, then make a GET request to obtain the data. When the data is obtained we then set it as the new state of the component and render it for display.

class Wrapper extends React.Component {   state = {     isLoading: true,     error: null,     list: []   };    fetchData() {     axios.get(this.props.link)       .then((response) => {         this.setState({           list: response.data,           isLoading: false         });     })     .catch(error => this.setState({ error, isLoading: false }));   }    componentDidMount() {     this.setState({ isLoading: true }, this.fetchData);   }    render() {     return this.props.render(this.state);   } }

The data link will be passed as props to the Wrapper component. When we get the response from the server, we update list using what is returned from the server. The request is made to the server after the component mounts.

Here is how the Wrapper gets used:

class App extends React.Component {   render() {     return (       <Wrapper         link="https://jsonplaceholder.typicode.com/users"         render={({ list, isLoading, error }) => (           <div>             <h2>Random Users</h2>             {error ? <p>{error.message}</p> : null}             {isLoading ? (               <h2>Loading...</h2>             ) : (               <ul>{list.map(user => <li key={user.id}>{user.name}</li>)}</ul>             )}           </div>         )}       />     );   } }

You can see that we pass the link as a prop, then we use ES6 de-structuring to get the state of the Wrapper component which is then rendered. The first time the component loads, we display loading text, which is replaced by the list of items once we get a response and data from the server.

The App component here is a class component since it does not manage state. We can transform it into a functional stateless component.

const App = () => {   return (     <Wrapper       link="https://jsonplaceholder.typicode.com/users"       render={({ list, isLoading, error }) => (         <div>           <h2>Random Users</h2>           {error ? <p>{error.message}</p> : null}           {isLoading ? (             <h2>Loading...</h2>           ) : (             <ul>{list.map(user => <li key={user.id}>{user.name}</li>)}</ul>           )}         </div>       )}     />   ); }

That’s a wrap!

People often compare render props with higher-order components. If you want to go down that path, I suggest you check out this post as well as this insightful talk on the topic by Michael Jackson.

The post An Overview of Render Props in React appeared first on CSS-Tricks.

CSS-Tricks

Posted on Leave a comment

A Guide to Custom Elements for React Developers

I had to build a UI recently and (for the first time in a long while) I didn’t have the option of using React.js, which is my preferred solution for UI these days. So, I looked at what the built-in browser APIs had to offer and saw that using custom elements (aka Web Components) may just be the remedy that this React developer needed.

Custom elements can offer the same general benefits of React components without being tied to a specific framework implementation. A custom element gives us a new HTML tag that we can programmatically control through a native browser API.

Let’s talk about the benefits of component-based UI:

  • Encapsulation – concerns scoped to that component remain in that component’s implementation
  • Reusability – when the UI is separated into more generic pieces, they’re easier to break into patterns that you’re more likely to repeat
  • Isolation – because components are designed to be encapsulated and with that, you get the added benefit of isolation, which allows you scope bugs and changes to a particular part of your application easier

Use cases

You might be wondering who is using custom elements in production. Notably:

  • GitHub is using custom elements for their modal dialogs, autocomplete and display time.
  • YouTube’s new web app is built with Polymer and web components.

Similarities to the Component API

When trying to compare React Components versus custom elements, I found the APIs really similar:

  • They’re both classes that aren’t “new” and are able that extend a base class
  • They both inherit a mounting or rendering lifecycle
  • They both take static or dynamic input via props or attributes

Demo

So, let’s build a tiny application that lists details about a GitHub repository.

Screenshot of end result

If I were going to approach this with React, I would define a simple component like this:

<Repository name="charliewilco/obsidian" />

This component takes a single prop — the name of the repository — and we implement it like this:

class Repository extends React.Component {   state = {     repo: null   };    async getDetails(name) {     return await fetch(`https://api.github.com/repos/$ {name}`, {       mode: 'cors'     }).then(res => res.json());   }    async componentDidMount() {     const { name } = this.props;     const repo = await this.getDetails(name);     this.setState({ repo });   }    render() {     const { repo } = this.state;      if (!repo) {       return <h1>Loading</h1>;     }      if (repo.message) {       return <div className="Card Card--error">Error: {repo.message}</div>;     }      return (       <div class="Card">         <aside>           <img             width="48"             height="48"             class="Avatar"             src={repo.owner.avatar_url}             alt="Profile picture for $ {repo.owner.login}"           />         </aside>         <header>           <h2 class="Card__title">{repo.full_name}</h2>           <span class="Card__meta">{repo.description}</span>         </header>       </div>     );   } }

See the Pen React Demo – GitHub by Charles (@charliewilco) on CodePen.

To break this down further, we have a component that has its own state, which is the repo details. Initially, we set it to be null because we don’t have any of that data yet, so we’ll have a loading indicator while the data is fetched.

During the React lifecycle, we’ll use fetch to go get the data from GitHub, set up the card, and trigger a re-render with setState() after we get the data back. All of these different states the UI takes are represented in the render() method.

Defining / Using a Custom Element

Doing this with custom elements is a little different. Like the React component, our custom element will take a single attribute — again, the name of the repository — and manage its own state.

Our element will look like this:

<github-repo name="charliewilco/obsidian"></github-repo> <github-repo name="charliewilco/level.css"></github-repo> <github-repo name="charliewilco/react-branches"></github-repo> <github-repo name="charliewilco/react-gluejar"></github-repo> <github-repo name="charliewilco/dotfiles"></github-repo>

See the Pen Custom Elements Demo – GitHub by Charles (@charliewilco) on CodePen.

To start, all we need to do to define and register a custom element is create a class that extends the HTMLElement class and then register the name of the element with customElements.define().

class OurCustomElement extends HTMLElement {} window.customElements.define('our-element', OurCustomElement);

And we can call it:

<our-element></our-element>

This new element isn’t very useful, but with custom elements, we get three methods to expand the functionality of this element. These are almost analogous to React’s lifecycle methods for their Component API. The two lifecycle-like methods most relevant to us are the disconnectedCallBack and the connectedCallback and since this is a class, it comes with a constructor.

Name Called when
constructor An instance of the element is created or upgraded. Useful for initializing state, settings up event listeners, or creating Shadow DOM. See the spec for restrictions on what you can do in the constructor.
connectedCallback The element is inserted into the DOM. Useful for running setup code, such as fetching resources or rendering UI. Generally, you should try to delay work until this time
disconnectedCallback When the element is removed from the DOM. Useful for running clean-up code.

To implement our custom element, we’ll create the class and set up some attributes related to that UI:

class Repository extends HTMLElement {   constructor() {     super();      this.repoDetails = null;      this.name = this.getAttribute("name");     this.endpoint = `https://api.github.com/repos/$ {this.name}`         this.innerHTML = `<h1>Loading</h1>`   } }

By calling super() in our constructor, the context of this is the element itself and all the DOM manipulation APIs can be used. So far, we’ve set the default repository details to null, gotten the repo name from element’s attribute, created an endpoint to call so we don’t have to define it later and, most importantly, set the initial HTML to be a loading indicator.

In order to get the details about that element’s repository, we’re going to need to make a request to GitHub’s API. We’ll use fetch and, since that’s Promise-based, we’ll use async and await to make our code more readable. You can learn more about the async/await keywords here and more about the browser’s fetch API here. You can also tweet at me to find out whether I prefer it to the Axios library. (Hint, it depends if I had tea or coffee with my breakfast.)

Now, let’s add a method to this class to ask GitHub for details about the repository.

class Repository extends HTMLElement {   constructor() {     // ...   }    async getDetails() {     return await fetch(this.endpoint, { mode: "cors" }).then(res => res.json());   } }

Next, let’s use the connectedCallback method and the Shadow DOM to use the return value from this method. Using this method will do something similar as when we called Repository.componentDidMount() in the React example. Instead, we’ll override the null value we initially gave this.repoDetails — we’ll use this later when we start to call the template to create the HTML.

class Repository extends HTMLElement {   constructor() {     // ...   }    async getDetails() {     // ...   }    async connectedCallback() {     let repo = await this.getDetails();     this.repoDetails = repo;     this.initShadowDOM();   }    initShadowDOM() {     let shadowRoot = this.attachShadow({ mode: "open" });     shadowRoot.innerHTML = this.template;   } }

You’ll notice that we’re calling methods related to the Shadow DOM. Besides being a rejected title for a Marvel movie, the Shadow DOM has its own rich API worth looking into. For our purposes, though, it’s going to abstract the implementation of adding innerHTML to the element.

Now we’re assigning the innerHTML to be equal to the value of this.template. Let’s define that now:

class Repository extends HTMLElement {   get template() {     const repo = this.repoDetails;        // if we get an error message let's show that back to the user     if (repo.message) {       return `<div class="Card Card--error">Error: $ {repo.message}</div>`     } else {       return `       <div class="Card">         <aside>           <img width="48" height="48" class="Avatar" src="$ {repo.owner.avatar_url}" alt="Profile picture for $ {repo.owner.login}" />         </aside>         <header>           <h2 class="Card__title">$ {repo.full_name}</h2>           <span class="Card__meta">$ {repo.description}</span>         </header>       </div>       `     }   } }

That’s pretty much it. We’ve defined a custom element that manages its own state, fetches its own data, and reflects that state back to the user while giving us an HTML element to use in our application.

After going through this exercise, I found that the only required dependency for custom elements is the browser’s native APIs rather than a framework to additionally parse and execute. This makes for a more portable and reusable solution with similar APIs to the frameworks you already love and use to make your living.

There are drawbacks of using this approach, of course. We’re talking about various browser support issues and some lack of consistency. Plus, working with DOM manipulation APIs can be very confusing. Sometimes they are assignments. Sometimes they are functions. Sometimes those functions take a callback and sometimes they don’t. If you don’t believe me, take a look at adding a class to an HTML element created via document.createElement(), which is one of the top five reasons to use React. The basic implementation isn’t that complicated but it is inconsistent with other similar document methods.

The real question is: does it even out in the wash? Maybe. React is still pretty good at the things it’s designed to be very very good at: the virtual DOM, managing application state, encapsulation, and passing data down the tree. There’s next to no incentive to use custom elements inside that framework. Custom elements, on the other hand, are simply available by virtue of building an application for the browser.

Learn more

The post A Guide to Custom Elements for React Developers appeared first on CSS-Tricks.

CSS-Tricks