Hamburger Menu with a Side of React Hooks and Styled Components

We all know what a hamburger menu is, right? When the pattern started making its way into web designs, it was both mocked and applauded for its minimalism that allows main menus to be tucked off screen, particularly on mobile where every pixel of space counts.

CSS-Tricks is all about double the meat.

Love ‘em or hate ‘em, hamburger menus are here and likely will be for some time to come. The problem is how to implement them. Sure, they look simple and straightforward, but they can be anything but. For example, should they be paired with a label? Are they more effective on the left or right side of the screen? How do we tackle closing those menus, whether by click or touch? Should the icon be an SVG, font, Unicode character, or pure CSS? What about a meatless option?

I wanted to build one of those but failed to find a simple solution. Most solutions are based on libraries, like reactjs-popup or react-burger-menu. They are great, but for more complex solutions. What about the core use case of a three-line menu that simply slides a panel out from the side of the screen when it’s clicked, then slides the panel back in when it’s clicked again?

I decided to build my own simple hamburger with sidebar. No pickles, onions or ketchup. Just meat, bun, and a side of menu items.

Are you ready to create it with me?

Here’s what we’re making

See the Pen
Burger menu with React hooks and styled-components
by Maks Akymenko (@maximakymenko)
on CodePen.

We’re building use React for this tutorial because it seems like a good use case for it: we get a reusable component and a set of hooks we can extend to handle the click functionality.

Spin up a new React project

Let’s spin up a new project using create-react-app, change to that folder directory and add styled-components to style the UI:

npx create-react-app your-project-name
cd your-project-name
yarn add styled-components

Add basic styles

Open the newly created project in your favorite code editor and start adding basic styles using styled-components. In your src directory, create a file called global.js. It will contain styles for the whole app. You can write your own or just copy what I ended up doing:

// global.js
import { createGlobalStyle } from 'styled-components';

export const GlobalStyles = createGlobalStyle`
  html, body {
    margin: 0;
    padding: 0;
  }
  *, *::after, *::before {
    box-sizing: border-box;
  }
  body {
    align-items: center;
    background: #0D0C1D;
    color: #EFFFFA;
    display: flex;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
    height: 100vh;
    justify-content: center;
    text-rendering: optimizeLegibility;
  }
  `

This is only a part of global styles, the rest of it you can find here.

The CreateGlobalStyle function is generally used for creating global styles that are exposed to the whole app. We’ll import it so we have access to these styles as we go.

The next step is to add a theme file that holds all our variables. Create a theme.js file in the src directory and add following:

// theme.js
export const theme = {
  primaryDark: '#0D0C1D',
  primaryLight: '#EFFFFA',
  primaryHover: '#343078',
  mobile: '576px',
}

Add layout, menu and hamburger components 🍔

Go to your App.js file. We’re going to wipe everything out of there and create the main template for our app. Here’s what I did. You can certainly create your own.

// App.js
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { GlobalStyles } from './global';
import { theme } from './theme';

function App() {
  return (
    <ThemeProvider theme={theme}>
      <>
        <GlobalStyles />
        <div>
          <h1>Hello. This is burger menu tutorial</h1>
          <img src="https://image.flaticon.com/icons/svg/2016/2016012.svg" alt="burger icon" />
          <small>Icon made by Freepik from www.flaticon.com</small>
        </div>
      </>
    </ThemeProvider>
  );
}
export default App;

Don’t forget to add the line with the small tag. That’s how we credit flaticon.comhttp://flaticon.com) authors for the provided icon.

Here’s what we’ve got up to this point:

Let me explain a little bit. We imported ThemeProvider, which is a wrapper component that uses the Context API behind the scenes to make our theme variables available to the whole component tree.

We also imported our GlobalStyles and passed them as a component to our app, which means that our application now has access to all global styles. As you can see, our GlobalStyles component is inside ThemeProvider which means we can already make some minor changes in it.

Go to global.js and change the background and color properties to use our defined variables. This helps us implement a theme rather than using fixed values that are difficult to change.

// global.js
background: ${({ theme }) => theme.primaryDark};
color: ${({ theme }) => theme.primaryLight};

We destructure our theme from props. So, instead of writing props.theme each time, we’re using a bunch of brackets instead. I’ll repeat myself: the theme is available because we’ve wrapped our global styles with ThemeProvider.

Create Burger and Menu components

Create a components folder inside the src directory and add two folders in there: Menu and Burger, plus an index.js file.

index.js will be used for one purpose: allow us to import components from one file, which is very handy, especially when you have a lot of them.

Now let’s create our components. Each folder will contain three files.

What’s up with all the files? You’ll see the benefit of a scalable structure soon enough. It worked well for me in a couple of projects, but here is good advice how to create scalable structure.

Go to the Burger folder and create Burger.js for our layout. Then add Burger.styled.js, which will contain styles, and index.js. which will exporting the file.

// index.js
export { default } from './Burger';

Feel free to style burger toggle in a way you want, or just paste these styles:

// Burger.styled.js
import styled from 'styled-components';

export const StyledBurger = styled.button`
  position: absolute;
  top: 5%;
  left: 2rem;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  width: 2rem;
  height: 2rem;
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0;
  z-index: 10;
  
  &:focus {
    outline: none;
  }
  
  div {
    width: 2rem;
    height: 0.25rem;
    background: ${({ theme }) => theme.primaryLight};
    border-radius: 10px;
    transition: all 0.3s linear;
    position: relative;
    transform-origin: 1px;
  }
`;

The transform-origin property will be needed later to animate the menu it toggles between open and closed states.

After adding the styles, go to Burger.js and add the layout:

// Burger.js
import React from 'react';
import { StyledBurger } from './Burger.styled';

const Burger = () => {
  return (
    <StyledBurger>
      <div />
      <div />
      <div />
    </StyledBurger>
  )
}

export default Burger;

After that look at the left top corner. Do you see it?

Time to do the same with the Menu folder:

// Menu -> index.js
export { default } from './Menu';

// Menu.styled.js
import styled from 'styled-components';

export const StyledMenu = styled.nav`
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: ${({ theme }) => theme.primaryLight};
  height: 100vh;
  text-align: left;
  padding: 2rem;
  position: absolute;
  top: 0;
  left: 0;
  transition: transform 0.3s ease-in-out;
  
  @media (max-width: ${({ theme }) => theme.mobile}) {
    width: 100%;
  }

  a {
    font-size: 2rem;
    text-transform: uppercase;
    padding: 2rem 0;
    font-weight: bold;
    letter-spacing: 0.5rem;
    color: ${({ theme }) => theme.primaryDark};
    text-decoration: none;
    transition: color 0.3s linear;
    
    @media (max-width: ${({ theme }) => theme.mobile}) {
      font-size: 1.5rem;
      text-align: center;
    }

    &:hover {
      color: ${({ theme }) => theme.primaryHover};
    }
  }
`;

Next, let’s add the layout for the menu items that are revealed when clicking on our burger:

// Menu.js
import React from 'react';
import { StyledMenu } from './Menu.styled';

const Menu = () => {
  return (
    <StyledMenu>
      <a href="/">
        <span role="img" aria-label="about us">&#x1f481;&#x1f3fb;&#x200d;&#x2642;&#xfe0f;</span>
        About us
      </a>
      <a href="/">
        <span role="img" aria-label="price">&#x1f4b8;</span>
        Pricing
        </a>
      <a href="/">
        <span role="img" aria-label="contact">&#x1f4e9;</span>
        Contact
        </a>
    </StyledMenu>
  )
}
export default Menu;

We’ve got nice emojis here, and best practice is to make them accessible by wrapping each one in a span and adding a couple of properties: role="img" and aria-label="your label". You can read more about it here.

Time to import our new components into our App.js file:

// App.js
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { GlobalStyles } from './global';
import { theme } from './theme';
import { Burger, Menu } from './components';

// ...

Let’s see, what we’ve got:

Take a look at this nice navigation bar! But we’ve got one issue here: it’s opened, and we want it initially to be closed. We only need to add one line to Menu.styled.js fix it:

// Menu.styled.js
transform: 'translateX(-100%)';

We are well on our way to calling this burger cooked! But first…

Adding open and close functionality

We want to open the sidebar when clicking the hamburger icon, so let’s get to it. Open App.js and add some state to it. We will use the useState hook for it.

// App.js
import React, { useState } from 'react';

After you import it, let’s use it inside the App component.

// App.js
const [open, setOpen] = useState(false);

We set the initial state to false, because our menu should be hidden when the application is rendered.

We need both our toggle and sidebar menu to know about the state, so pass it down as a prop to each component. Now your App.js should look something like this:

// App.js
import React, { useState } from 'react';
import { ThemeProvider } from 'styled-components';
import { GlobalStyles } from './global';
import { theme } from './theme';
import { Burger, Menu } from './components';

function App() {
  const [open, setOpen] = useState(false);
  return (
    <ThemeProvider theme={theme}>
      <>
        <GlobalStyles />
        <div>
          <h1>Hello. This is burger menu tutorial</h1>
          <img src="https://media.giphy.com/media/xTiTnwj1LUAw0RAfiU/giphy.gif" alt="animated burger" />
        </div>
        <div>
          <Burger open={open} setOpen={setOpen} />
          <Menu open={open} setOpen={setOpen} />
        </div>
      </>
    </ThemeProvider>
  );
}
export default App;

Notice that we’re wrapping our components in a div. This will be helpful later when we add functionality that closes the menu when clicking anywhere on the screen.

Handle props in the components

Our Burger and Menu know about the state, so all we need to do is to handle it inside and add styles accordingly. Go to Burger.js and handle the props that were passed down:

// Burger.js
import React from 'react';
import { bool, func } from 'prop-types';
import { StyledBurger } from './Burger.styled';
const Burger = ({ open, setOpen }) => {
  return (
    <StyledBurger open={open} onClick={() => setOpen(!open)}>
      <div />
      <div />
      <div />
    </StyledBurger>
  )
}
Burger.propTypes = {
  open: bool.isRequired,
  setOpen: func.isRequired,
};
export default Burger;

We destructure the open and setOpen props and pass them to our StyledBurger to add styles for each prop, respectively. Also, we add the onClick handler to call our setOpen function and toggle open prop. At the end of the file, we add type checking, which is considered a best practice for aligning arguments with expected data.

You can check whether it works or not by going to your react-dev-tools. Go to Components tab in your Chrome DevTools and click on Burger tab.

Now, when you click on your Burger component, (don’t mix it up with the tab), you should see, that your open checkbox is changing its state.

Go to Menu.js and do almost the same, although, here we pass only the open prop:

// Menu.js
import React from 'react';
import { bool } from 'prop-types';
import { StyledMenu } from './Menu.styled';
const Menu = ({ open }) => {
  return (
    <StyledMenu open={open}>
      <a href="/">
        <span role="img" aria-label="about us">&#x1f481;&#x1f3fb;&#x200d;&#x2642;&#xfe0f;</span>
        About us
      </a>
      <a href="/">
        <span role="img" aria-label="price">&#x1f4b8;</span>
        Pricing
        </a>
      <a href="/">
        <span role="img" aria-label="contact">&#x1f4e9;</span>
        Contact
        </a>
    </StyledMenu>
  )
}
Menu.propTypes = {
  open: bool.isRequired,
}
export default Menu;

Next step is to pass open prop down to our styled component so we could apply the transition. Open Menu.styled.js and add the following to our transform property:

transform: ${({ open }) => open ? 'translateX(0)' : 'translateX(-100%)'};

This is checking if our styled component open prop is true, and if so, it adds translateX(0) to move our navigation back on the screen. You can already test it out locally!

Wait, wait, wait!

Did you notice something wrong when checking things out? Our Burger has the same color as the background color of our Menu, which make them blend together. Let’s change that and also animate the icon a bit to make it more interesting. We’ve got the open prop passed to it, so we can use that to apply the changes.

Open Burger.styled.js and write the following:

// Burger.styled.js
import styled from 'styled-components';
export const StyledBurger = styled.button`
  position: absolute;
  top: 5%;
  left: 2rem;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  width: 2rem;
  height: 2rem;
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0;
  z-index: 10;

  &:focus {
    outline: none;
  }

  div {
    width: 2rem;
    height: 0.25rem;
    background: ${({ theme, open }) => open ? theme.primaryDark : theme.primaryLight};
    border-radius: 10px;
    transition: all 0.3s linear;
    position: relative;
    transform-origin: 1px;

    :first-child {
      transform: ${({ open }) => open ? 'rotate(45deg)' : 'rotate(0)'};
    }

    :nth-child(2) {
      opacity: ${({ open }) => open ? '0' : '1'};
      transform: ${({ open }) => open ? 'translateX(20px)' : 'translateX(0)'};
    }

    :nth-child(3) {
      transform: ${({ open }) => open ? 'rotate(-45deg)' : 'rotate(0)'};
    }
  }
`;

This is a big chunk of CSS, but it makes the animation magic happen. We check if the open prop is true and change styles accordingly. We rotate, translate, then hide the menu icon’s lines while changing color. Beautiful, isn’t it?

Okay, folks! By now, you should know how to create a simple hamburger icon and menu, that incorporates responsiveness and smooth animation. Congratulations!

But there’s one last thing we ought to account for...

Close the menu by clicking outside of it

This part seems like a small bonus, but it’s a big UX win because it allows the user to close the menu by clicking anywhere else on the page. This helps the user avoid having to re-locate the menu icon and clicking exactly on it.

We’re going to put more React hooks to use to make this happen! Create a file in the src directory, called hooks.js and open it. For this one, we’re gonna turn to the useEffect hook, which was introduced in React 18.

// hooks.js
import { useEffect } from 'react';

Before we write the code, let’s think about the logic behind this hook. When we click somewhere on the page, we need to check whether the clicked element is our current element (in our case, that is the Menu component) or if the clicked element contains the current element (for instance, our div that wraps our menu and hamburger icon). If so, we don’t do anything, otherwise, we call a function, that we’ll name handler.

We are going to use ref to check the clicked element, and we will do so every time someone clicks on the page.

// hooks.js
import { useEffect } from 'react';

export const useOnClickOutside = (ref, handler) => {
  useEffect(() => {
    const listener = event => {
      if (!ref.current || ref.current.contains(event.target)) {
        return;
      }
      handler(event);
    };
    document.addEventListener('mousedown', listener);
    return () => {
      document.removeEventListener('mousedown', listener);
    };
  },
  [ref, handler],
  );
};

Don’t forget to return the function from useEffect. That’s so-called "clean up" and, basically, it stands for removing an event listener when the component unmounts. It is the replacement of componentWillUnmount lifecycle.

Now let’s hook up the hook

We’ve got our hook ready, so it’s time to add it to the app. Go to the App.js file, and import two hooks: the newly created useOnClickOutside and also useRef. We’ll need the latter to get a reference to the element.

// App.js
import React, { useState, useRef } from 'react';
import { useOnClickOutside } from './hooks';

To get access these in the current element, we need to get access to the DOM node. That’s where we use useRef, also, the name node perfectly reflects the point of this variable.

From there, we pass the node as a first argument. We’ll pass the function that closes our menu as a second argument.

// App.js
const node = useRef(); 
useOnClickOutside(node, () => setOpen(false));

Lastly, we need to pass our ref to the DOM element. In our case, it will be div, that holds the Burger and Menu components:

// App.js
<div ref={node}>
  <Burger open={open} setOpen={setOpen} />
  <Menu open={open} setOpen={setOpen} />
</div>

Your App.js should look similar to this:

// App.js
import React, { useState, useRef } from 'react';
import { ThemeProvider } from 'styled-components';
import { useOnClickOutside } from './hooks';
import { GlobalStyles } from './global';
import { theme } from './theme';
import { Burger, Menu } from './components';
function App() {
  const [open, setOpen] = useState(false);
  const node = useRef();
  useOnClickOutside(node, () => setOpen(false));
  return (
    <ThemeProvider theme={theme}>
      <>
        <GlobalStyles />
        <div>
          <h1>Hello. This is burger menu tutorial</h1>
          <img src="https://media.giphy.com/media/xTiTnwj1LUAw0RAfiU/giphy.gif" alt="animated burger" />
        </div>
        <div ref={node}>
          <Burger open={open} setOpen={setOpen} />
          <Menu open={open} setOpen={setOpen} />
        </div>
      </>
    </ThemeProvider>
  );
}
export default App;

Check this out! It works as expected, and it’s fully functional and responsive.

Congratulations, everyone! You did a great job! Happy coding!

View on GitHub

The post Hamburger Menu with a Side of React Hooks and Styled Components appeared first on CSS-Tricks.

Using requestAnimationFrame with React Hooks

Animating with requestAnimationFrame should be easy, but if you haven’t read React’s documentation thoroughly then you will probably run into a few things that might cause you a headache. Here are three gotcha moments I learned the hard way.

TLDR: Pass an empty array as a second parameter for useEffect to avoid it running more than once and pass a function to your state’s setter function to make sure you always have the correct state. Also, use useRef for storing things like the timestamp and the request’s ID.

useRef is not only for DOM references

There are three ways to store variables within functional components:

  1. We can define a simple const or let whose value will always be reinitialized with every component re-rendering.
  2. We can use useState whose value persists across re-renderings, and if you change it, it will also trigger re-rendering.
  3. We can use useRef.

The useRef hook is primarily used to access the DOM, but it’s more than that. It is a mutable object that persists a value across multiple re-renderings. It is really similar to the useState hook except you read and write its value through its .current property, and changing its value won’t re-render the component.

For instance, the example below will always show 5 even if the component is re-rendered by its parent.

function Component() {
  let variable = 5;

  setTimeout(() => {
    variable = variable + 3;
  }, 100)

  return <div>{variable}</div>
}

...whereas this one will keep increasing the number by three and keeps re-rendering even if the parent does not change.

function Component() {
  const [variable, setVariable] = React.useState(5);

  setTimeout(() => {
    setVariable(variable + 3);
  }, 100)

  return <div>{variable}</div>
}

And finally, this one returns five and won’t re-render. However, if the parent triggers a re-render then it will have an increased value every time (assuming the re-render happened after 100 milliseconds).

function Component() {
  const variable = React.useRef(5);

  setTimeout(() => {
    variable.current = variable.current + 3;
  }, 100)

  return <div>{variable.current}</div>
}

If we have mutable values that we want to remember at the next or later renders and we don’t want them to trigger a re-render when they change, then we should use useRef. In our case, we will need the ever-changing request animation frame ID at cleanup, and if we animate based on the the time passed between cycles, then we need to remember the previous animation’s timestamp. These two variables should be stored as refs.

The side effects of useEffect

We can use the useEffect hook to initialize and cleanup our requests, though we want to make sure it only runs once; otherwise it’s really easy to end up doubling the amount of the animation frame requests with every animation cycle. Here’s a bad example:

function App() {
  const [state, setState] = React.useState(0)

  const requestRef = React.useRef()
  
  const animate = time => {
    // Change the state according to the animation
    requestRef.current = requestAnimationFrame(animate);
  }
    
  // DON’T DO THIS
  React.useEffect(() => {
    requestRef.current = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(requestRef.current);
  });
  
  return <div>{state}</div>;
}

Why is it bad? If you run this, the useEffect will trigger the animate function that will both change the state and request a new animation frame. It sounds good, except that the state change will re-render the component by running the whole function again including the useEffect hook that will spin up a new request in parallel with the one that was already requested by the animate function in the previous cycle. This will ultimately end up in doubling our animation frame requests each cycle. Ideally, we only have 1 at a time. In the case above, if we assume 60 frame per second then we’ll have 1,152,921,504,606,847,000 animation frame requests in parallel after only one second.

To make sure the useEffect hook runs only once, we can pass an empty array as a second argument to it. Passing an empty array has a side-effect though, which avoids us from having the correct state during animation. The second argument is a list of changing values that the effect needs to react to. We don’t want to react to anything — we only want to initialize the animation — hence we have the empty array. But React will interpret this in a way that means this effect doesn’t have to be up to date with the state. And that includes the animate function because it was originally called from the effect. As a result, if we try to get the value of the state in the animate function, it will always be the initial value. If we want to change the state based on its previous value and the time passed, then it probably won’t work.

function App() {
  const [state, setState] = React.useState(0)

  const requestRef = React.useRef()
  
  const animate = time => {
    // The 'state' will always be the initial value here
    requestRef.current = requestAnimationFrame(animate);
  }
    
  React.useEffect(() => {
    requestRef.current = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(requestRef.current);
  }, []); // Make sure the effect runs only once
  
  return <div>{state}</div>;
}

The state’s setter function also accepts a function

There’s a way to use our latest state even if the useEffect hook locked our state to its initial value. The setter function of the useState hook can also accept a function. So instead of passing a value based on the current state as you probably would do most of the time:

setState(state + delta)

... you can also pass on a function that receives the previous value as a parameter. And, yes, that’s going to return the correct value even in our situation:

setState(prevState => prevState + delta)

Putting it all together

Here’s a simple example to wrap things up. We’re going to put all of the above together to create a counter that counts up to 100 then restarts from the beginning. Technical variables that we want to persist and mutate without re-rendering the whole component are stored with useRef. We made sure useEffect only runs once by passing an empty array as its second parameter. And we mutate the state by passing on a function to the setter of useState to make sure we always have the correct state.

See the Pen
Using requestAnimationFrame with React hooks
by Hunor Marton Borbely (@HunorMarton)
on CodePen.

The post Using requestAnimationFrame with React Hooks appeared first on CSS-Tricks.

Awesome Demos Roundup #7

So many awesome work has been done over the past month! There’s lots of demos and experiments that will make your jaw drop, that’s for sure. From procedurally generated CSS numbers to amazing CSS animations, there’s much to explore and wonder about.

We hope you enjoy this creative compilation!

Dandelion

Dandelion

Anime inspired self portrait

Anime-inspired-self-portrait

Walking Sprites Demo

Walking-Sprites-Demo

Ghibli Slider

Ghibli-Slider

∞ Loader animation

Loader-animation

Analog Clock in Three.js

Analog-Clock-in-Three

Toast Pop!

Toast-Pop

Wobbly underline

Wobbly-underline

Plasma

Plasma

Liquidfun

ShakeLiquidWindow

Kalli Hero

Kalli-Hero

Highway Race

Highway-Race

Cassie!

Cassie

anime.js grid staggering demo

AnimeJSStaggering

Distortion and Parallax Shader

DistortionParallaxShader

Fireworks

Fireworks

Grid Jr. (Web Audio API + Canvas visualizer)

Grid-Jr.-Web-Audio-API-+-Canvas-visualizer

Paint Drop Hover

Paint-Drop-Hover

Fishing Game

Fishing-Game

Clip Clop Clippity Clop

Clip-Clop-Clippity-Clop

Text by circles

Text-by-circles

Social Links Cube (PlayCanvas)

Social-Links-Cube-PlayCanvas

Color Search w/ React Hooks

Color-Search-wReact-Hooks

React Slider w/ Hover Effect

React-Slider-wHover-Effect

30 – 50 hogs

30—50-hogs

GLSL: 3D rotational spatial modification

GLSL-3D-rotational-spatial-modification

Procedurally Generated CSS Numbers

Procedurally-Generated-CSS-Numbers

Plibt

plibt

Glitched circles

glitched-circles

ThreeJS Hover Zoom Channel Displacement

ThreeJS-Hover-Zoom-Channel-Displacement

react-three-fiber: suspense + GLTF loader

threejs_PaulHenschel

Awesome Demos Roundup #7 was written by Mary Lou and published on Codrops.

Run useEffect Only Once

React has a built-in hook called useEffect. Hooks are used in function components. The Class component comparison to useEffect are the methods componentDidMount, componentDidUpdate, and componentWillUnmount.

useEffect will run when the component renders, which might be more times than you think. I feel like I've had this come up a dozen times in the past few weeks, so it seems worthy of a quick blog post.

import React, { useEffect } from 'react';

function App() {
  useEffect(() => {
    // Run! Like go get some data from an API.
  });

  return (
    <div>
      {/* Do something with data. */}
    </div>
  );
}

In a totally isolated example like that, it's likely the useEffect will run only once. But in a more complex app with props flying around and such, it's certainly not guaranteed. The problem with that is that if you're doing something like fetching data from an API, you might end up double-fetching which is inefficient and unnecessary.

The trick is that useEffect takes a second parameter.

The second param is an array of variables that the component will check to make sure changed before re-rendering. You could put whatever bits of props and state you want in here to check against.

Or, put nothing:

import React, { useEffect } from 'react';

function App() {
  useEffect(() => {
    // Run! Like go get some data from an API.
  }, []);

  return (
    <div>
      {/* Do something with data. */}
    </div>
  );
}

That will ensure the useEffect only runs once.

Note from the docs:

If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders.

The post Run useEffect Only Once appeared first on CSS-Tricks.

How to Use GraphQL in React Using Hooks

What are Hooks?

React Hooks, introduced in version 16.8.0, are reusable stateful logic functions. They aim to simplify the development of complex components by splitting them into small functional blocks that are easier to manage, test, and reuse.

Using hooks removes the need for many abstractions like Higher Order Components (HOC) and render props. They allow you to add functionality to the application without having to change the component hierarchy and or encapsulate components.

Build a Chat App Using React Hooks in 100 Lines of Code

We’ve looked at React Hooks before, around here at CSS-Tricks. I have an article that introduces them as well that illustrates how to use them to create components through functions. Both articles are good high-level overviews about the way they work, but they open up a lot of possibilities, too.

So, that’s what we’re going to do in this article. We’re going to see how hooks make our development process easier and faster by building a chat application.

Specifically, we are building a chat application using Create React App. While doing so, we will be using a selection of React Hooks to simplify the development process and to remove a lot of boilerplate code that’s unnecessary for the work.

There are several open source Reacts hooks available and we’ll be putting those to use as well. These hooks can be directly consumed to build features that otherwise would have taken more of code to create. They also generally follow well-recognized standards for any functionality. In effect, this increases the efficiency of writing code and provides secure functionalities.

Let’s look at the requirements

The chat application we are going to build will have the following features:

  • Get a list of past messages sent from the server
  • Connect to a room for group chatting
  • Get updates when people disconnect from or connect to a room
  • Send and receive messages

We’re working with a few assumptions as we dive in:

  • We’ll consider the server we are going to use as a blackbox. Don't worry about it working perfectly as we're going to communicate with it using simple sockets.
  • All the styles are contained in a single CSS file, can be copied to the src directory. All the styles used within the app are linked in the repository.

Getting set up for work

OK, we’re going to want to get our development environment ready to start writing code. First off, React requires both Node and npm. You can set them up here.

Let’s spin up a new project from the Terminal:

npx create-react-app socket-client
cd socket-client
npm start

Now we should be able to navigate to http://localhost:3000 in the browser and get the default welcome page for the project.

From here, we’re going to break the work down by the hooks we’re using. This should help us understand the hooks as we put them into practical use.

Using the setState hook

The first hook we're going to use is useState. It allows us to maintain state within our component as opposed to, say, having to write and initialize a class using this.state. Data that remains constant, such as username, is stored in useState variables. This ensures the data remains easily available while requiring a lot less code to write.

The main advantage of useState is that it's automatically reflected in the rendered component whenever we update the state of the app. If we were to use regular variables, they wouldn’t be considered as the state of the component and would have to be passed as props to re-render the component. So, again, we’re cutting out a lot of work and streamlining things in the process.

The hook is built right into React, so we can import it with a single line:

import React, { useState } from 'react';

We are going to create a simple component that returns "Hello" if the user is already logged in or a login form if the user is logged out. We check the id variable for that.

Our form submissions will be handled by a function we’re creating called handleSubmit. It will check if the Name form field is completed. If it is, we will set the id and room values for that user. Otherwise, we’ll throw in a message reminding the user that the Name field is required in order to proceed.

// App.js

import React, { useState } from 'react';
import './index.css';

export default () => {
  const [room, setRoom] = useState('');
  const [id, setId] = useState('');

  const handleSubmit = e => {
    e.preventDefault();
    const name = document.querySelector('#name').value.trim();
    const room_value = document.querySelector('#room').value.trim();
    if (!name) {
      return alert("Name can't be empty");
    }
    setId(name);
    setRoom(document.querySelector('#room').value.trim());
  };

  return id !== '' ? (
    <div>Hello</div>
  ) : (
    <div style={{ textAlign: 'center', margin: '30vh auto', width: '70%' }}>
      <form onSubmit={event => handleSubmit(event)}>
        <input id="name" required placeholder="What is your name .." /><br />
        <input id="room" placeholder="What is your room .." /><br />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
};

That’s how we’re using the useState hook in our chat application. Again, we’re importing the hook from React, constructing values for the user’s ID and chat room location, setting those values if the user’s state is logged in, and returning a login form if the user is logged out.

Using the useSocket hook

We're going to use an open source hook called useSocket to maintain a connection to our server. Unlike useState, this hook is not baked into React, so we’re going to have to add it to our project before importing it into the app.

npm add use-socket.io-client

The server connection is maintained by using the React Hooks version of the socket.io library, which is an easier way of maintaining websocket connections with a server. We are using it for sending and receiving real-time messages as well as maintaining events, like connecting to a room.

The default socket.io client library has global declarations, i.e., the socket variable we define can be used by any component. However, our data can be manipulated from anywhere and we won't know where those changes are happening. Socket hooks counter this by constraining hook definitions at the component level, meaning each component is responsible for its own data transfer.

The basic usage for useSocket looks like this:

const [socket] = useSocket('socket-url')

We’re going to be using a few socket APIs as we move ahead. For the sake of reference, all of them are outlined in the socket.io documentation. But for now, let’s import the hook since we’ve already installed it.

import useSocket from 'use-socket.io-client';

Next, we’ve got to initialize the hook by connecting to our server. Then we’ll log the socket in the console to check if it is properly connected.

const [id, setId] = useState('');
const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');

socket.connect();
console.log(socket);

Open the browser console and the URL in that snippet should be logged.

Using the useImmer hook

Our chat app will make use of the useImmer hook to manage state of arrays and objects without mutating the original state. It combines useState and Immer to give immutable state management. This will be handy for managing lists of people who are online and messages that need to be displayed.

Using Immer with useState allows us to change an array or object by creating a new state from the current state while preventing mutations directly on the current state. This offers us more safety as far as leaving the current state intact while being able to manipulate state based on different conditions.

Again, we’re working with a hook that’s not built into React, so let’s import it into the project:

npm add use-immer

The basic usage is pretty straightforward. The first value in the constructor is the current state and the second value is the function that updates that state. The useImmer hook then takes the starting values for the current state.

const [data, setData] = useImmer(default_value)

Using the setData hook

Notice the setData hook in that last example? We’re using that to make a draft copy of the current data we can use to manipulate the data safely and use it as the next state when changes become immutable. Thus, our original data is preserved until we’re done running our functions and we’re absolutely clear to update the current data.

setData(draftState => { 
  draftState.operation(); 
});

// ...or

setData(draft => newState);

// Here, draftState is a copy of the current data

Using the useEffect hook

Alright, we’re back to a hook that’s built right into React. We’re going to use the useEffect hook to run a piece of code only when the application loads. This ensures that our code only runs once rather than every time the component re-renders with new data, which is good for performance.

All we need to do to start using the hook is to import it — no installation needed!

import React, { useState, useEffect } from 'react';

We will need a component that renders a message or an update based on the presence or absence of a sende ID in the array. Being the creative people we are, let’s call that component Messages.

const Messages = props => props.data.map(m => m[0] !== '' ? 
(<li key={m[0]}><strong>{m[0]}</strong> : <div className="innermsg">{m[1]}</div></li>) 
: (<li key={m[1]} className="update">{m[1]}</li>) );

Let’s put our socket logic inside useEffect so that we don't duplicate the same set of messages repeatedly when a component re-renders. We will define our message hook in the component, connect to the socket, then set up listeners for new messages and updates in the useEffect hook itself. We will also set up update functions inside the listeners.

const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');      
socket.connect();

const [messages, setMessages] = useImmer([]);
useEffect(()=>{
  socket.on('update', message => setMessages(draft => {
    draft.push(['', message]);
  }));

  socket.on('message que',(nick, message) => {
    setMessages(draft => {
      draft.push([nick, message])
    })
  });
},0);

Another touch we’ll throw in for good measure is a "join" message if the username and room name are correct. This triggers the rest of the event listeners and we can receive past messages sent in that room along with any updates required.

// ...
  setRoom(document.querySelector('#room').value.trim());
  socket.emit('join', name, room);
};

return id ? (
  <section style={{display:'flex',flexDirection:'row'}} >
    <ul id="messages"><Messages data={messages}></Messages></ul>
    <ul id="online"> &#x1f310; :</ul>
    <div id="sendform">
      <form id="messageform" style={{display: 'flex'}}>
        <input id="m" /><button type="submit">Send Message</button>
      </form>
    </div>
  </section>
) : (
// ...

The finishing touches

We only have a few more tweaks to wrap up our chat app. Specifically, we still need:

  • A component to display people who are online
  • A useImmer hook for it with a socket listener
  • A message submission handler with appropriate sockets

All of this builds off of what we’ve already covered so far. I’m going to drop in the full code for the App.js file to show how everything fits together.

// App.js

import React, { useState, useEffect } from 'react'; 
import useSocket from 'use-socket.io-client'; 
import { useImmer } from 'use-immer';

import './index.css';

const Messages = props => props.data.map(m => m[0] !== '' ? (<li><strong>{m[0]}</strong> : <div className="innermsg">{m[1]}</div></li>) : (<li className="update">{m[1]}</li>) );

const Online = props => props.data.map(m => <li id={m[0]}>{m[1]}</li>);

export default () => { 
  const [room, setRoom] = useState(''); 
  const [id, setId] = useState('');
  
  const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');
  socket.connect();

  const [messages, setMessages] = useImmer([]);
  
  const [online, setOnline] = useImmer([]);
  
  useEffect(()=>{
    socket.on('message que',(nick,message) => {
      setMessages(draft => {
        draft.push([nick,message])
      })
    });
  
    socket.on('update',message => setMessages(draft => {
      draft.push(['',message]);
    }))
  
    socket.on('people-list',people => {
      let newState = [];
      for(let person in people){
        newState.push([people[person].id,people[person].nick]);
      }
      setOnline(draft=>{draft.push(...newState)});
      console.log(online)
    });
  
    socket.on('add-person',(nick,id)=>{
      setOnline(draft => {
        draft.push([id,nick])
      })
    })
  
    socket.on('remove-person',id=>{
      setOnline(draft => draft.filter(m => m[0] !== id))
    })
  
    socket.on('chat message',(nick,message)=>{
      setMessages(draft => {draft.push([nick,message])})
    })
  },0);
  
  const handleSubmit = e => {
    e.preventDefault();
    const name = document.querySelector('#name').value.trim();
      const room_value = document.querySelector('#room').value.trim();
    if (!name) {
      return alert("Name can't be empty");
    }
    setId(name);
    setRoom(document.querySelector('#room').value.trim());
    console.log(room)
    socket.emit("join", name,room_value);
  };
  
  const handleSend = e => {
    e.preventDefault();
    const input = document.querySelector('#m');
    if(input.value.trim() !== ''){
      socket.emit('chat message',input.value,room);
      input.value = '';
    }
  }
  
  return id ? (
    <section style={{display:'flex',flexDirection:'row'}} >
      <ul id="messages"><Messages data={messages} /></ul>
      <ul id="online"> &#x1f310; : <Online data={online} /> </ul>
      <div id="sendform">
        <form onSubmit={e => handleSend(e)} style={{display: 'flex'}}>
            <input id="m" /><button style={{width:'75px'}} type="submit">Send</button>
        </form>
      </div>
    </section>
  ) : (
    <div style={{ textAlign: 'center', margin: '30vh auto', width: '70%' }}>
      <form onSubmit={event => handleSubmit(event)}>
        <input id="name" required placeholder="What is your name .." /><br />
        <input id="room" placeholder="What is your room .." /><br />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
};

Wrapping up

That's it! We built a fully functional group chat application together! How cool is that? The complete code for the project can be found here on GitHub.

What we’ve covered in this article is merely a glimpse of how React Hooks can boost your productivity and help you build powerful applications with powerful front-end tooling. I have built a more robust chat application in this comprehensive tutorial. Follow along if you want to level up further with React Hooks.

Now that you have hands-on experience with React Hooks, use your newly gained knowledge to get even more practice! Here are a few ideas of what you can build from here:

  • A blogging platform
  • Your own version of Instagram
  • A clone of Reddit

Have questions along the way? Leave a comment and let’s make awesome things together.

The post Build a Chat App Using React Hooks in 100 Lines of Code appeared first on CSS-Tricks.

Getting to Know the useReducer React Hook

useReducer is one of a handful of React hooks that shipped in React 16.7.0. It accepts a reducer function with the application initial state, returns the current application state, then dispatches a function.

Here is an example of how it is used;

const [state, dispatch] = useReducer(reducer, initialState);

What’s the good for? Well, think about any situation where having the first loaded state of the application might be nice. Let’s say the starting point on an interactive map. Maybe it’s an app that lets the user build a custom car with custom options from a default model. Here’s a pretty neat demo of a calculator app that puts useRedcuer to use in order to reset the calculator to a default state of zero when clearing it out.

See the Pen
Basic React Hook Calculator
by Gianpierangelo De Palma (@dpgian)
on CodePen.

We’re going to dig into a couple more examples in this post, but let’s first look at the hook itself to get a better idea of what it is and what exactly it does when it’s used.

The almighty reducer

It’s tough to talk about useState without also mentioning JavaScript’s reduce method. We linked it up at the very top, but Sarah’s post is an excellent overview of reducers and helps set the state for where we’re going here.

The first and most important thing to understand about a reducer is that it will always only return one value. The job of a reducer is to reduce. That one value can be a number, a string, an array or an object, but it will always only be one. Reducers are really great for a lot of things, but they're especially useful for applying a bit of logic to a group of values and ending up with another single result.

So, if we have an array of numbers, reduce will distill it down to a single number that adds up for as many times as there are values. Say we have this simple array:

const numbers = [1, 2, 3]

...and we have a function that logs each time our reducer makes a calculation into the console. This will help us see how reduce distills the array into a single number.

const reducer = function (tally, number) { 
	console.log(`Tally: ${tally}, Next number: ${number}, New Total: ${tally + number}`)
	return tally + number
}

Now let’s run a reducer on it. As we saw earlier, reduce takes dispatches a function that runs against a default state. Let’s plug our reducer function and an initial value of zero in there.

const total = numbers.reduce(reducer, 0)

Here’s what gets logged to the console:

"Tally: 0, Next number: 1, New Total: 1"
"Tally: 1, Next number: 2, New Total: 3"
"Tally: 3, Next number: 3, New Total: 6"

See how reduce takes an initial value and builds on it as each number in the array is added to it until we get a final value? In this case, that final value is 6.

I also really like this (modified) example from Dave Ceddia that shows how reduce can be used on an array of letters to spell a word:

var letters = ['r', 'e', 'd', 'u', 'c', 'e'];

// `reduce` takes 2 arguments:
//   - a function to do the reducing (you might say, a "reducer")
//   - an initial value for accumulatedResult
var word = letters.reduce(
	function(accumulatedResult, arrayItem) {
		return accumulatedResult + arrayItem;
	},
''); // <-- notice this empty string argument: it's the initial value

console.log(word) // => "reduce"

useReducer works with states and actions

OK, that was a lot of refresher to get what we’re really talking about: userReducer. It’s important to get all this, though, because you may have noticed where we’re going now after having seen the way reduce fires a function against an initial value. It’s the same sort of concept, but returns two elements as an array, the current state and a dispatch function.

In other words:

const [state, dispatch] = useReducer(reducer, initialArg, init);

What’s up with that third init argument? It’s an optional value that will lazily create the initial state. That means we can calculate the initial state/value with an init function outside of the reducer instead of providing an explicit value. That’s handy if the initial value could be different, say based on a last saved state instead of a consistent value.

To get it working, we need to do a few things:

  • Define an initial state.
  • Provide a function that contains actions that update the state.
  • Trigger userReducer to dispatch an updated state that’s calculated relative to the initial state.

The classic example of this a counter application. In fact, that’s what React’s docs use to drive the concept home. Here’s that put into practice:

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

It’s a good example because it demonstrates how an initial state (a zero value) is used to calculate a new value each time an action is fired by clicking either the increase or decrease button. We could even throw in a “Reset" button in there to clear the total back to the initial state of zero.

Example: A Car Customizer

See the Pen
React useReducer - car example
by Geoff Graham (@geoffgraham)
on CodePen.

In this example, we are making the assumption that the user has selected a car to purchase. However, we want the app to allow the user to add extra options to the car. Each option has a price that adds to the base total.

First, we need to create the initial state which will consist of the car, an empty array to keep track of features, and an additional price that starts at $26,395 and a list of items in the store, so the user can pick what they want.

const initialState = {
  additionalPrice: 0,
  car: {
    price: 26395,
    name: "2019 Ford Mustang",
    image: "https://cdn.motor1.com/images/mgl/0AN2V/s1/2019-ford-mustang-bullitt.jpg",
    features: []
  },
  store: [
    { id: 1, name: "V-6 engine", price: 1500 },
    { id: 2, name: "Racing detail package", price: 1500 },
    { id: 3, name: "Premium sound system", price: 500 },
    { id: 4, name: "Rear spoiler", price: 250 }
  ]
};

Our reducer function will handle two things: the addition and removal of new items.

const reducer = (state, action) => {
  switch (action.type) {
    case "REMOVE_ITEM":
      return {
        ...state,
        additionalPrice: state.additionalPrice - action.item.price,
        car: { ...state.car, features: state.car.features.filter((x) => x.id !== action.item.id)},
        store: [...state.store, action.item]
      };
    case "BUY_ITEM":
      return {
        ...state,
        additionalPrice: state.additionalPrice + action.item.price,
        car: { ...state.car, features: [...state.car.features, action.item] },
        store: state.store.filter((x) => x.id !== action.item.id)
      }
    default:
      return state;
  }
}

When the user selects the item she wants, we update the features for the car, increase the additionalPrice and also remove the item from the store. We ensure that the other parts of the state remain as they are.
We do something similar when a user removes an item from the features list - reduce the additional price, return the item to the store.
Here is how the App component looks like.

const App = () => {
  const inputRef = useRef();
  const [state, dispatch] = useReducer(reducer, initialState);
  
  const removeFeature = (item) => {
    dispatch({ type: 'REMOVE_ITEM', item });
  }
  
  const buyItem = (item) => {
    dispatch({ type: 'BUY_ITEM', item })
  }
  
  return (
    <div>
      <div className="box">
        <figure className="image is-128x128">
          <img src={state.car.image} />
        </figure>
        <h2>{state.car.name}</h2>
        <p>Amount: ${state.car.price}</p>
        <div className="content">
          <h6>Extra items you bought:</h6>
          {state.car.features.length ? 
            (
              <ol type="1">
                {state.car.features.map((item) => (
                  <li key={item.id}>
                    <button
                      onClick={() => removeFeature(item)}
                      className="button">X
                    </button>
                    {item.name}
                  </li>
                ))}
              </ol>
            ) : <p>You can purchase items from the store.</p>
          }
        </div>
      </div>
      <div className="box">
        <div className="content">
          <h4>Store:</h4>
          {state.store.length ? 
            (
            <ol type="1">
              {state.store.map((item) => (
                <li key={item.id}>\
                  <button
                    onClick={() => buyItem(item)}
                    className="button">Buy
                  </button>
                  {item.name}
                </li>
              ))}
            </ol>
            ) : <p>No features</p>
          }
        </div>

        <div className="content">
        <h4>
          Total Amount: ${state.car.price + state.additionalPrice}
        </h4>
      </div>
      </div>
    </div>
  );
}

The actions that get dispatched contains the details of the selected item. We make use of the action type to determine how the reducer function will handle the updating of the state. You can see that the rendered view changes based on what you do - buying an item from the store removes the item from the store and adds it to the list of features. Also, the total amount gets updated. No doubt, there are some improvements that can be done to the application, this is only for learning purpose.

What about useState? Can’t we use that instead?

An astute reader may have been asking this all along. I mean, setState is generally the same thing, right? Return a stateful value and a function to re-render a component with that new value.

const [state, setState] = useState(initialState);

We could have even used the useState() hook in the counter example provided by the React docs. However, useReducer is preferred in cases where state has to go through complicated transitions. Kent C. Dodds wrote up a explanation of the differences between the two and (while he often reaches for setState) he provides a good use case for using userReducer instead:

If your one element of your state relies on the value of another element of your state, then it's almost always best to use useReducer

For example, imagine you have a tic-tac-toe game you're writing. You have one element of state called squares which is just an array of all the squares and their value[.]

My rule of thumb is to reach for useReducer to handle complex states, particularly where the initial state is based on the state of other elements.

Oh wait, we already have Redux for this!

Those of you who have worked with Redux already know everything we’ve covered here and that’s because it was designed to use the Context API to pass stored states between components — without having to pass props through other components to get there.

So, does useReducer replace Redux? Nope. I mean, you can basically make your own Redux by using it with the useContext hook, but that’s doesn’t mean Redux is useless; Redux still has plenty of other features and benefits worth considering.

Where have you used userReducer? Have you found clear-cut cases where it’s better than setState? Maybe you can experiment with the things we covered here to build something. Here are a few ideas:

  • A calendar that focus at today’s date but allows a user to select other dates. Maybe even add a “Today" button that returns the user to today’s date.
  • You can try improving on the car example - have a list of cars that users can purchase. You might have to define this in the initial state, then the user can add extra features they want with a charge. These features can be predefined, or defined by the user.

The post Getting to Know the useReducer React Hook appeared first on CSS-Tricks.

Add User Authentication to Your React App

Savvy frontend devs use React components to scaffold their web apps efficiently and get to market faster. The React team's component-based approach has turned frontend development into a simple exercise akin to building a tower of blocks, aka their reusable and extendable components. You can find a React component for just about any need nowadays. We've even built an Okta React component that allows you to easily add user authentication to your app.

In this tutorial, you’ll be creating a fun React app that fetches random Chuck Norris jokes. I’ll also show you how to add user authentication using Okta and customize your user experience, so the jokes replace "Chuck Norris" with your user's names.

How To Start Using Sketch And Framer X

How To Start Using Sketch And Framer X

How To Start Using Sketch And Framer X

Martina Pérez

When it comes to showing the transition, interaction and animation of elements in the user interface, a prototyping tool like Framer X can make a difference in the way you communicate your vision to the team and stakeholders and as a result, boost your efficiency as a designer.

With the following example, I will illustrate how you can add interaction to static designs. To profit the most from this tutorial, some basic experience with Framer X are welcome.

To get started, you will need the following tools and assets:

Note: This tutorial is for designers using MacOS, as Framer X is for Mac (although this may change in the near future), and Sketch app is also only for Mac.

Bringing Your Designs From Sketch

We will bring our designs from Sketch into Framer X. You might also create your designs in Framer X as there is a full bunch of layout tools to do so, but there are some reasons whereby you can be interested in continuing designing your interfaces in Sketch:

  • You are used to Sketch and not willing to learn a new design tool.
  • You have already some designs in Sketch and you want to build the interaction for them.
  • Sketch works much better with large files. Framer X seems to have some problems when moving elements around.
  • Framer X is still in his early stages as a design tool and there are not many tutorials about how to create some elements of design. On the other hand, there are plenty of tutorials about Sketch and also many plugins such as the Craft plugin for Sketch which allows you to speed up your design process.
  • You will find many more web design resources for Sketch than for Framer X.
  • There is still a lack of tutorials about some of the options available in Framer X, especially about how to use the Code components.

So first of all, let’s create a new file in Framer X. The first thing we will do is to bring the design screen we have in Sketch into Framer X. To do so, just copy the Static artboard from Sketch and paste it in Framer X.

Layout in Sketch and the same layout in Framer X, once imported.
Copy your designs from Sketch and paste them into Framer X. (Large preview)

As you can see in the Layers panel, we’ve got the same layers we had in Sketch.

Note: Sometimes, when bringing your designs into Framer X, some properties may be lost — in this case, it’s the border radius of the thumbnails. This is because we used a Mask in Sketch and Framer X doesn’t recognize it. To solve this, you can either select the Group in Sketch and Flatten it to Bitmap before pasting it into Framer X, or once you have brought the design into Framer X, double-click to reach the Frame element and manually change the Radius in the Properties panel.

Organize The Layout In Framer

To work on the interaction of the elements in Framer X, we need to create a new Frame.

Note: A frame is similar to an Artboard in Sketch (or to the HTML <div> element), but is more powerful. Frames act more like containers as a frame can contain other frames.

To create a new Frame, go to the Layout tool panel, select Frame and drag and drop in any part of the canvas; or simply press F.

To create a Frame, select Frame and drag and drop in the canvas.
To create a new Frame, select Frame in the Layout tool panel. (Large preview)

Second, we will set a device for the Frame. Select the Frame and in the Properties panel, Device, choose Apple MacBook Pro.

Select a Frame, Device in the Properties panel and choose Apple MacBook Pro.
To set a device for a Frame, select a Device in the Properties panel. (Large preview)

Take a look at the Static frame we’ve imported. We will build the following interactions:

  • Header: Fade in when scrolling through the content.
  • Floating Action Button (FAB): Default, hover and pressed states.
  • Nav: It is displayed from right to left when clicking the FAB.
  • Content: It is resized when clicking the FAB.
Highlighting the elements we will build the interaction for: header, nav, FAB and content.
We will build interactions for the header, nav, FAB, and content. (Large preview)

First, create new Frames on top of the layers in such a way that you end up having one Frame for each of the abovemention interaction elements. For instance, we will add a new Frame to group all the layers that are part of the Header (Cmd + Enter) and name the Frame as header.

Select the elements of the header and add a Frame on top of them.
Group the layers to have one Frame for each of our interactive elements. (Large preview)

For the sake of clarity, change the name of the Apple MacBook Pro frame to Interactive. In the following section we will create a Scroll component.

Scroll Through The Content

First, we will duplicate (Cmd + D) the Content frame and take it out of the Static frame. Change the name of the new frame to i_content.

Content Frame has to be out of the Static frame.
Duplicate the Content frame and take it out of the Static frame. (Large preview)

Secondly, we will create a Scroll component in our Interactive frame. To do so, go to the Interactive tool panel and select Scroll. Drag and drop in any part of the Interactive frame.

In addition to that, modify the width of the Scroll component to 1141px (same as the Content) and position it in the same coordinates as the Content is on the Static frame (left: 149px, top: 140px). Apart from that, increase the height of the Scroll component as it reaches the bottom of the Interactive frame.

Position the Scroll component where it was on the Static frame.
Modify the width and height of the Scroll component and position it in the same coordinates as the Content frame. (Large preview)

Next, we will connect the Scroll component to our i_content. To do so, click on the connector and connect it to i_content.

Connect the Scroll component to the i_content.
Connect the Scroll component to the i_content. (Large preview)

Lastly, select the Interactive frame and Cmd + P to enter the Preview Mode. You should be able to scroll through the content now.

Scrolling through the page on Preview Mode.
To access to Preview Mode, press Cmd + P. (Large preview)

Next, I will explain how to position the Header and the FAB (Floating Action Button) button to make them fixed while scrolling with no need for any special coding.

Fixed Elements

We will position the elements so as they remain fixed when scrolling through the page. To that end, duplicate the Header frame and position it in the Interactive frame. As before, change the name to i_header. Do the same with the Floating Action Button button. Your Layers panel should look like this.

i_fab and i_header are in the Interactive frame.
Duplicate the Header and the FAB frames and position them within the Interactive frame. (Large preview)

As the Header and the FAB are out of the Scroll component, they will remain fixed while scrolling.

In Preview mode, Scroll through the content, whereas the header and FAB remain fixed.
You can scroll through the content, whereas the header and FAB remain fixed. (Large preview)

In the next section, I will explain how to build the transition of the Header.

Header Transition

To build the transition of the Header we will make use of the Scroll away component created by Lukas Guschlbauer. To start using this component, go to the Framer Store, search for Scroll and install the Scroll Away component.

Framer store where you can search the Scroll Away component.
Search for 'Scroll' in the Framer Store and install the Scroll Away component. (Large preview)

Next, go to the the Components panel, click the Scroll Away and drop it onto the canvas. Take the Header frame out of the Interactive frame and position the Scroll away component where the Header was. Change the component size as to be the same as the Header (1440x80px).

Now, select the Scroll Away component and connect it to your i_header frame. You can change multiple properties of the component such as alignment, effect, direction, easing or timing in the Properties panel. We will change the effect to Fade Move. Once that is done, the options below will change accordingly.

Applying the Fade Move effect in the Scroll Away component.
Take the Header frame out of the Interactive frame and change the effect to Fade Move. (Large preview)

For the effect to work, we need one more thing. Select the Scroll Away Component and click the Override in the Properties panel. Click File and select New File. Then, click Override and select useScrollData. Next, click the Scroll component of your Interactive frame. Click Override again and select getScrollData in Override. To preview the result, press Cmd + P.

Note: Overrides are a unique concept to Framer X. Code overrides are functions that allow components to communicate with each other. You can write them yourself in code and apply them to any Frame or component on your canvas. These functions allow you to override visual properties like opacity and fill, and allow for interactivity and animation. Code overrides can live in any code file in your project. Framer X interprets these based on the type. You can apply any code override to any Frame or component on the canvas by selecting Override from the Properties panel.

There is a white space at top when scrolling through the content in Preview Mode
The transition of the Header works, but there is a white space at the top. (Large preview)

The transition works, but you will notice that there is a white space at the top. This is because the Scroll component is positioned at y: 140. Let’s change this. Increase the height of the Scroll component to occupy the whole height of the Interactive frame. Next, go to your i_content frame and position the elements 140px from the top of the frame.

Positioning the content 140px from top in the i_content frame.
In the i_content frame, position the content 140px from top. (Large preview)

Your interaction should be now similar to this one.

Header goes away with Fade Move effect while scrolling through the content in Preview Mode.
Header transition when scrolling through the content. (Large preview)

Note: If you need it, download the source file for this step.

Next, I will explain how to change the state of a button when interacting with it.

States For Buttons

In this section, we will work on the different states for the Floating Action Button (FAB). We will make use of the Magic Move component created by Henrique Gusso. So first of all, go to the Framer store and install this component. In the Components panel, select the component and drop it on the canvas.

At first, select the Floating Action Button frame and take it out of the Interactive frame. Next, position the Magic Move component where the FAB was. Change its size to 72×72px.

The i_fab is out of the Interactive frame and the Magic Move component is where the FAB was.
Take the FAB out of the Interactive frame and position the Magic Move component where the FAB was. (Large preview)

For some reason, the graphic imported from Sketch doesn’t work with this component, so we will create our own circle. In the i_fab frame, remove the frame that contains the graphic and create a circle with the same color and properties instead.

Detail of the layers of the i_fab frame.
Remove the graphic imported from Sketch and create your own circle. (Large preview)

For the different states of the FAB, we need to create a Master and three Instances (default, hover, and pressed states). To do so, select the i_FAB, right-click on it, and select Create component.

Right click on the i_fab, Create component to get a Master of the element.
To create a Master, select the i_fab frame, right-click, Create component. (Large preview)

Note: Design Components are similar to Symbols in Sketch. So if you want to create reusable components for your Design System this is a very useful tool. To change the properties of your instances all at once, just select the Master and modify the properties you want. For further information, take a look at the Framer X guide to create Design Components.

Now, duplicate the i_fab Master frame 3 times to get the instances. Change the name of the new frames to i_fab_default, i_fab_hover, and i_fab_pressed.

The Master and the three instances of the i_fab: default, hover and pressed.
To create the Instances for the states of the button, duplicate the Master 3 times. (Large preview)

Next, we need to connect the Magic Move component to the instances. Connect Initial to i_fab_default, Hover Start to i_fab_hover, Tap to i_fab_pressed and Hover End to i_fab_default.

Connect the Magic Move component in the Interactive frame to the instances: default, hover and pressed.
Connect the Magic Move component to the instances. (Large preview)

Finally, we have to go into each one of the states and change the color and scale. Go into the i_fab_hover and change its color to #2244BF. To do so, double click until you see the Fill option in the Properties panel. Next, go into the i_fab_pressed, reduce its size to 56px and change its color to #172E80. Check the result in the Preview Mode.

Note: If you see a black screen in the Preview Mode, check the compatibility of the component with your Framer Library Version. To do so, go to the page of the component in the Framer Store. If the package is not compatible with your version you will see a warning message. To change your Framer Library, navigate to File → Framer Library Version.

Different states of the FAB in Preview Mode while hovering and clicking it.
To access the Preview mode, press Cmd + P. (Large preview)

In the next section I will explain how to display the nav from right to left by clicking the FAB button.

Note: If you need it, download the source file for this step.

Open The Nav When Clicking A Button

Now, we will work on the interaction to display the Nav when clicking the FAB button. We will make use of the Link to and the Magic Move component again for the transition.

Go to the Static frame and duplicate the Nav frame. Position it in any part of the canvas and change its name to i_nav.

i_nav is out of the Static frame.
Duplicate the Nav frame, take it out of the Static frame and change its name to i_nav. (Large preview)

Now, we need to create two different states for the Nav. First, the initial state (no nav is shown) and second state, the nav is displayed. To do so, create a new Master with the i_nav (right-click and select Create component, or use the shortcut Cmd + K). Once you have the Master, duplicate two times to get the Instances. Name them i_nav_default and i_nav_displayed.

The master of i_nav with the two instances: default and displayed.
Duplicate the Master to get the instances of i_nav. (Large preview)

Next, position the elements of the i_nav_default out of the frame (Right: -80px).

i_nav_default is positioned out of the frame.
Position the i_nav_default out of the frame. (Large preview)

So, the Nav will be displayed automatically from right to left when clicking the FAB. To build this animation, we need to create a new Frame. Duplicate the Interactive frame. Change the name of the new Frame to Interactive02.

Interactive frame and its duplicate: Interactive02 frame.
Duplicate the Interactive frame. (Large preview)

To display the Nav in this new Frame we will create a new Magic Move component. The size (80x392px) and the position (Top: 140, Right: 0) need to be the same as the Nav.

Magic Move component in the Interactive02 frame.
Add a new Magic Move component in the Interactive02 frame. (Large preview)

Next, go to Interactive02 frame, select the Magic Move Component and connect it to the instances. i_nav_default to the Initital state and i_nav_displayed to the Automatic state. By doing so, when entering on this second Frame, the Nav will be displayed automatically.

Connect the Magic Move component in Interactive02 frame to the instances of the i_nav.
Connect the Magic Move component to the instances. (Large preview)

Now, we will build the interaction between screens by linking the Frames. Go to the Interactive frame, select the FAB, right-click → Add Frame. Change the name of the new frame to interactive_fab. Press L (Link to) and connect it to the Interactive02 frame.

Linking the FAB to Interactive02 frame.
On the Interactive frame, add a new Frame on top of the FAB and press L to connect it to the Interactive02 frame. (Large preview)

Change the transition to Instant and preview it. You can change the transition effect between screens in the Properties panel.

Select the FAB and changing the transition to Instant.
Change the transition to Instant. (Large preview)

If you go to Preview Mode you will see that the Nav is shown when clicking the FAB button, but we need to reverse the interaction when clicking again on it. To do so, duplicate the Interactive02 frame (give the name Interactive03 to the new Frame).

On Interactive03 frame, select the Magic Move component and assign i_nav_displayed to the Initial State and i_nav_default to the Automatic State.

Assigning states of the i_nav to the Magic Move Component on Interactive03 frame.
Assign i_nav_displayed to the Initial State and i_nav_default to Automatic state on Interactive03 frame. (Large preview)

Finally, press L (Link to) the interactive_fab in the Interactive02 frame to Interactive03 frame and the interactive_fab in Interactive03 frame to the Interactive02 frame. Remember to change the transition effect in the Link properties to Instant.

Linking screens with the FAB in Interactive02 frame and Interactive03 frame.
Link the interactive_fab in the Interactive02 frame to Interactive03 frame and the interactive_fab in Interactive03 to the Interactive02 frame. (Large preview)

Preview the interaction from the Interactive frame. The result should be as the one below:

When clicking the FAB button, the Nav is displayed from right to left.
When clicking the FAB button, the Nav is displayed from right to left. (Large preview)

Note: If you need it, download the source file for this step.

Next, I will explain how to resize the content when clicking the FAB button.

Resize The Content When The Nav Is Displayed

To resize the content when the Nav is displayed we will write some React code. We will use Playground (a code editor integrated in Framer X) that allows you to play with some React and HTML code to build advanced animations.

Note: If you are not familiar with React, you may be interested in taking a look at some React tutorials.

So first, go to each of the interactive frames, select the Scroll component and add a Frame on top of it.

A Frame was added on top of the Scroll component.
Add a Frame on top of each of the Scroll components. (Large preview)

Next, go to the Properties panel and click Override. On File, select App. If you don’t see it, select New file. Then, click Edit Code. The Playground will be opened automatically.

Make sure your App file has the following line at the top. If not, add it:

import { Data, animate, Override, Animatable } from "framer"

Note: (*) This code works with Framer v. 25 and the latest API version v. 1.0.7. While this code works, keep in mind that Framer is now encouraging users to use React Hooks functions instead of class components. Learn more at the new Framer API documentation pages.

We will declare a variable called contentScaleValue and indicate that it can be animated. Also, we will set the default toggle state as true.

//Value by default
const contentScaleValue = Animatable(1);
let toggle = true;

Then, we will create a function ResizeContent for the Content to be scaled when clicking the FAB. Besides that, we have to set its originX and originY as so it scales from top left.

//Assign to content
export const ResizeContent: Override = props => {
 return {
   scale: contentScaleValue,
   originX: 0,
   originY: 0
 };
};

Next, we will create a second function togglePosition for the FAB. We will say that onTap, if toggle is true, Content will be rescaled and the toggle state will change to false. Otherwise, do the reverse animation.

//Assign to FAB
export const togglePosition: Override = props => {
 return {
   onTap: () => {
     if (toggle) {
       animate.ease(contentScaleValue, 0.9, {duration: 0.2});
       toggle = false;
     } else {
       animate.ease(contentScaleValue, 1, {duration: 0.2});
       toggle = true;
     }
   }
 };
};

After writing this code, select the Frame you created on top of your Scroll components, go to the Override section in the properties panel and select File: App, Override: ResizeContent. Next, select the FAB, File: App, Override: togglePosition.

The Override is set to ResizeContent on the Scroll component. The Override is set to togglePosition on the FAB component.
Select the Frame on top of your Scroll components and set the Override as ResizeContent. Select the FAB and set the Override to togglePosition. (Large preview)

Check that the result works as the following.

The Content is resized when clicking the FAB in Preview Mode.
To preview the interaction, press Cmd + P. (Large preview)

Note: If you need it, download the source file for this step.

Prototyping

Go to the the Sketch file and import (copy and paste) the User profile artboard into Framer X. Once in Framer, duplicate the Frame. Give it the name Interactive_user_profile.

User profile artboard in Sketch and once imported in Framer X.
Copy your User profile artboard in Sketch and paste it into Framer X. Then duplicate your frame and give it the name 'Interactive_user_profile'. (Large preview)

We will build automatic transitions for the right sidebar, the boxes at top and the content. Take each of the Frames out of the Interactive_user_profile frame.

Highlighting the elements that we will build the interaction for: right sidebar, boxes at top and content.
We will build automatic transitions for the right sidebar, the boxes at top and the content. (Large preview)

Create a Magic Move component for each of the elements and position them in the Interactive_user_profile frame.

Magic Move components for the elements are in Interactive_user_profile frame.
Create a Magic Move component for the elements to be animated. (Large preview)

Next, create a Master and two instances for each of the components. For the sidebar, position the first instance out of the Frame. For the boxes and the content, change the opacity of the first instance to 0.

A Master and two instances for each of the components. The opacity of the second instance is set to 0.
Create a Master and two instances for each of the components. (Large preview)

Connect the Magic Move component to each of the instances. Assign the first instance to the Initial State and the second instance to the Automatic State.

The Magic Move components are connected to the instances of the elements.
Connect the Magic Move components to the instances. (Large preview)

In the Properties panel, you can change the delay of each of the Move Magic components to set up the order to be shown. Assign the next delays:

  • Sidebar: no delay
  • First box: 0.4 delay
  • Second box: 0.6 delay
  • Third box: 0.8 delay
  • Content: 1 delay.
Selecting one of the boxes and setting a delay of 0.4.
Change the delay of the components in the Properties panel. (Large preview)

Go to the Interactive03 frame, select the header and Link to the Interactive_user_profile frame. In this frame, select the header and Link to Interactive frame. Remember to change the transition between the screens to Instant. Check the results.

Linking Interactive03 frame to Interactive_user_profile frame.
Prototyping with the Link tool. (Large preview)

Note: If you need it, download the source file for this step.

Clicking on the header leads you to User profile frame where the elements are displayed with an automatic animation.
Cmd + P to check the result. (Large preview)

Sharing The Prototype

To share the prototype, click FileExport Web Preview (Cmd + E). To see the prototype, open the index.html. It will launch the prototype in a web browser.

Conclusion And Takeaways

If you are looking for a design tool specialized in interaction, Framer X is the perfect one. Framer X allows you to build simple transitions between screens, micro interactions, but also to design complex interactions making use of React code.

By using Framer X, you will speed up your design process and will be able to better communicate the interaction of your designs to the team and stakeholders.

  • You can still design the interface in Sketch and paste your designs into Framer X to build the interaction of the elements there.
  • For this tutorial, I have have been using a design imported from Sketch, but you can create your layouts in Framer X as well.
  • There are multiple UI kits available in the Framer Store to help you build your design systems.
  • There is no single approach on how to build an interaction in Framer X. Experiment and learn.
  • To build some quick interactions faster, make use of the pre-built components in the Framer Store.
  • You just need a minimal coding knowledge in order to start building complex interactions in Framer X and there are multiple tutorials available online to start learning how to do it.
Smashing Editorial (mb, yk, il)

React.js Tutorial: Let’s “Hook” Up

With the introduction of React 16.8 in 2018, the React team came up with the concept of “Hooks.” In this post, we are going to explore the reasons behind creating hooks and how to use them in a React application.

In React, we can create two types of components: Functional/Stateless components and Class/Stateful components.

Collective #505





C505_CSSBattle

CSSBattle

Use your CSS skills to replicate targets with smallest possible code.

Play it











C505_gimli

Gimli

A promising looking Visual Studio Code extension enabling smart visual tools for front-end developers. You can back it on Kickstarter.

Check it out




C505_xray

x-ray

In case you didn’t know it: A simple HTML debugger, executable by bookmark, that let’s you visualize the HTML structure of a website.

Check it out





C505_shoperr

shoperr

With shoperr you can create your own custom shop in minutes, and stock up on referral links to your favorite products.

Check it out


Collective #505 was written by Pedro Botelho and published on Codrops.

Monthly Web Development Update 3/2019: React Hooks, Constructable Stylesheets, And Building Trust

Monthly Web Development Update 3/2019: React Hooks, Constructable Stylesheets, And Building Trust

Monthly Web Development Update 3/2019: React Hooks, Constructable Stylesheets, And Building Trust

Anselm Hannemann

Do you sometimes feel like there’s so much to read and learn that your brain can’t take it anymore? It’s something most of us experience from time to time when we have too much to do and then overload our brains with even more. I’m aware that my reading lists aren’t helpful in that regard, as they contain even more things to learn. But it’s the very reason why I try to compile a diverse, open-minded set of articles that aren’t entirely frontend- or tech-related. And in weeks like this one where there aren’t too many articles for me to summarize, I realize how relieving this can be. Let’s give our brains the chance to wind down a bit when it tells us to and use the opportunity to reconsider how we do work.

Think about how you approach tasks, for example. Do you ask for more details when you’re given a specific task? Do you figure out how to do it yourself? Or are you just following the task’s details? Only doing the latter will get things done, of course. But it’ll also increase the risk that you forget about necessary details, as a study on storing passwords reveals now. If there’s nothing in the task description about hashing a password, for example, many people will not apply it, even if they know it’s the better solution. Or take the process of building a website: If we forget to add the correct caching, server costs will be unnecessarily high, and performance will suffer. It’s these little extra steps of thinking that make the difference between good, solid work and “just getting stuff done”.

News

  • Chrome 74 brings some new features to DevTools: It now highlights all elements that are affected by a CSS property, Lighthouse 4 is integrated into the Audits panel, and a WebSocket binary message viewer has been added, too.
  • Intersection Observer is still quite new and yet Chrome developers are currently introducing version 2 to tackle some common problems and implement learnings from the first version. Here’s what’s going to change in Intersection Observer v2.

General

  • It’s easy to forget about it, but even today we often build non-diverse solutions in many areas of life. This article shows how that happens with car crash test dummies that neglect women.
  • Voice is becoming more and more important in our lives, mainly because we use more devices without real display interfaces today — Homepod, Alexa, Siri, Google Assistant, or Amazon Echo. Mozilla teamed up with institutes from around the world to create an open-source pool of high-quality voices that helps teach machines how real people speak.
  • “In our modern world, it’s easy to junk things up. Simple is hard. We’re quick to add more questions to research surveys, more buttons to a digital interface, more burdens to people”. Kate Clayton explores how to be an elegant simplifier.
  • “People think that data is in the cloud, but it’s not. It’s in the ocean.” Let’s dive deep into how communication works and how it came to be that Microsoft, Google, Facebook, and Amazon own more than half of the undersea bandwidth. The article shows how the Internet depends on these big four companies nowadays and that we’d face massive struggles and performance impacts if we avoided them.
  • Jason Miller wrote an introduction to rendering on the web, summarizing what happens when a user accesses a website through a modern browser. There’s a lot to learn in here.
Map of the world showing undersea internet cables in 2021
Data is not in the cloud. It’s in the ocean. And more than half of the undersea bandwidth is owned by Amazon, Facebook, Google, and Microsoft. (Image credit)

UI/UX

  • Anand Satyan explains why it’s important to start designing without color first. It helps you understand the structure of data and layout better and often results in cleaner, more consistent designs.
  • Brad Frost wrote about the importance of providing forms that are simple, not clever, especially if you want users to log in.
  • Nikita Prokopov tried to analyze and redesign Github’s repository page. While I don’t like the final result too much, there are a lot of great takeaways from improving existing design patterns and the user experience with simple methods.

JavaScript

CSS

  • Constructable Stylesheets is a new way of initializing an external stylesheet or set of styles in a non-blocking way. This new approach allows us to dynamically construct stylesheets via JavaScript which is especially useful when we use it for Web Components in a ShadowDOM. The feature is available in Chrome Preview builds currently.
  • Rachel Andrew explains how we’re going to break boxes with the new CSS Fragmentation specification. With CSS Fragmentation, we can do things we used to do with float, but it’s more flexible and helps us control page breaks and other things relevant for print or eBooks.
  • This CSS-only experiment is mind-blowing. I’m seriously impressed and wouldn’t have imagined that we can do something like this with CSS today.

Security

Web Performance

Accessibility

  • Ben Robertson shares five tools we can use for automated accessibility audits. This is great because it allows us to use these tools in CIs, in regression testing (e.g., via Selenium or Chrome/Firefox headless browsers), or directly in our browsers.
  • Alex Carpenter summarized takeaways from WebAIM’s recent accessibility analysis of the top one million sites: 59% of form inputs are unlabeled and, thus, not accessible. Making them accessible for everyone wouldn’t be hard at all. It’s as easy as wrapping the input and describing it, for example like this: <label>Name<input name="name"></label> Of course, there are even better labeling practices out there, but this would already be enough to make a difference for all users of a website, not only those who rely on assistive technologies.
  • Accessibility Insights is a new platform service that provides developers with tools to analyze the accessibility of their web projects.
Cartoon cat and a laptop which is running the Accessibility Insights extension
The Accessibility Insights extension helps you spot accessibility errors and shows how to fix them. (Image credit)

Work & Life

  • How do we build trust as leaders? Claire Lew shares why business retreats and team building activities don’t matter much compared to the things that really make a difference: showing vulnerability, communicating the intent behind actions, and, finally, following through on commitments.
  • I found this article by Sahil Lavingia, the founder of Gumroad, very insightful. In it, he shares the failures, the struggles, and the bad decisions when getting Venture Capital, and why having a “normal” company is worth a thought, too, to prevent the whole thing from failing.
  • Our children are technology-focused and spend a lot of time in front of screens, playing games or watching videos. Pamela Paul advocates for letting our children get bored again.

Going Beyond…

Here’s one more thing: The periodic — yet not regular — reminder to give something back if you enjoy reading my writings and summary of articles. — Anselm

Smashing Editorial (cm)

What Hooks Mean for Vue

Not to be confused with Lifecycle Hooks, Hooks were introduced in React in v16.7.0-alpha, and a proof of concept was released for Vue a few days after. Even though it was proposed by React, it’s actually an important composition mechanism that has benefits across JavaScript framework ecosystems, so we’ll spend a little time today discussing what this means.

Mainly, Hooks offer a more explicit way to think of reusable patterns — one that avoids rewrites to the components themselves and allows disparate pieces of the stateful logic to seamlessly work together.

The initial problem

In terms of React, the problem was this: classes were the most common form of components when expressing the concept of state. Stateless functional components were also quite popular, but due to the fact that they could only really render, their use was limited to presentational tasks.

Classes in and of themselves present some issues. For example, as React became more ubiquitous, stumbling blocks for newcomers did as well. In order to understand React, one had to understand classes, too. Binding made code verbose and thus less legible, and an understanding of this in JavaScript was required. There are also some optimization stumbling blocks that classes present, discussed here.

In terms of the reuse of logic, it was common to use patterns like render props and higher-order components, but we’d find ourselves in similar “pyramid of doom” — style implementation hell where nesting became so heavily over-utilized that components could be difficult to maintain. This led me to ranting drunkenly at Dan Abramov, and nobody wants that.

Hooks address these concerns by allowing us to define a component's stateful logic using only function calls. These function calls become more compose-able, reusable, and allows us to express composition in functions while still accessing and maintaining state. When hooks were announced in React, people were excited — you can see some of the benefits illustrated here, with regards to how they reduce code and repetition:

In terms of maintenance, simplicity is key, and Hooks provide a single, functional way of approaching shared logic with the potential for a smaller amount of code.

Why Hooks in Vue?

You may read through this and wonder what Hooks have to offer in Vue. It seems like a problem that doesn’t need solving. After all, Vue doesn’t predominantly use classes. Vue offers stateless functional components (should you need them), but why would we need to carry state in a functional component? We have mixins for composition where we can reuse the same logic for multiple components. Problem solved.

I thought the same thing, but after talking to Evan You, he pointed out a major use case I missed: mixins can’t consume and use state from one to another, but Hooks can. This means that if we need chain encapsulated logic, it’s now possible with Hooks.

Hooks achieve what mixins do, but avoid two main problems that come with mixins:

  • They allows us to pass state from one to the other.
  • They make it explicit where logic is coming from.

If we’re using more than one mixin, it’s not clear which property was provided by which mixin. With Hooks, the return value of the function documents the value being consumed.

So, how does that work in Vue? We mentioned before that, when working with Hooks, logic is expressed in function calls that become reusable. In Vue, this means that we can group a data call, a method call, or a computed call into another custom function, and make them freely compose-able. Data, methods, and computed now become available in functional components.

Example

Let’s go over a really simple hook so that we can understand the building blocks before we move on to an example of composition in Hooks.

useWat?

OK, here’s were we have, what you might call, a crossover event between React and Vue. The use prefix is a React convention, so if you look up Hooks in React, you’ll find things like useState, useEffect, etc. More info here.

In Evan’s live demo, you can see where he’s accessing useState and useEffect for a render function.

If you’re not familiar with render functions in Vue, it might be helpful to take a peek at that.

But when we’re working with Vue-style Hooks, we’ll have — you guessed it — things like: useData, useComputed, etc.

So, in order for us look at how we'd use Hooks in Vue, I created a sample app for us to explore.

In the src/hooks folder, I've created a hook that prevents scrolling on a useMounted hook and reenables it on useDestroyed. This helps me pause the page when we're opening a dialog to view content, and allows scrolling again when we're done viewing the dialog. This is good functionality to abstract because it would probably be useful several times throughout an application.

import { useDestroyed, useMounted } from "vue-hooks";

export function preventscroll() {
  const preventDefault = (e) => {
    e = e || window.event;
    if (e.preventDefault)
      e.preventDefault();
    e.returnValue = false;
  }

  // keycodes for left, up, right, down
  const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };

  const preventDefaultForScrollKeys = (e) => {
    if (keys[e.keyCode]) {
      preventDefault(e);
      return false;
    }
  }

  useMounted(() => {
    if (window.addEventListener) // older FF
      window.addEventListener('DOMMouseScroll', preventDefault, false);
    window.onwheel = preventDefault; // modern standard
    window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
    window.touchmove = preventDefault; // mobile
    window.touchstart = preventDefault; // mobile
    document.onkeydown = preventDefaultForScrollKeys;
  });

  useDestroyed(() => {
    if (window.removeEventListener)
      window.removeEventListener('DOMMouseScroll', preventDefault, false);

    //firefox
    window.addEventListener('DOMMouseScroll', (e) => {
      e.stopPropagation();
    }, true);

    window.onmousewheel = document.onmousewheel = null;
    window.onwheel = null;
    window.touchmove = null;
    window.touchstart = null;
    document.onkeydown = null;
  });
} 

And then we can call it in a Vue component like this, in AppDetails.vue:

<script>
import { preventscroll } from "./../hooks/preventscroll.js";
...

export default {
  ...
  hooks() {
    preventscroll();
  }
}
</script>

We're using it in that component, but now we can use the same functionality throughout the application!

Two Hooks, understanding each other

We mentioned before that one of the primary differences between hooks and mixins is that hooks can actually pass values from one to another. Let's look at that with a simple, albeit slightly contrived, example.

Let's say in our application we need to do calculations in one hook that will be reused elsewhere, and something else that needs to use that calculation. In our example, we have a hook that takes the window width and passes it into an animation to let it know to only fire when we're on larger screens.

In the first hook:

import { useData, useMounted } from 'vue-hooks';

export function windowwidth() {
  const data = useData({
    width: 0
  })

  useMounted(() => {
    data.width = window.innerWidth
  })

  // this is something we can consume with the other hook
  return {
    data
  }
}

Then, in the second we use this to create a conditional that fires the animation logic:

// the data comes from the other hook
export function logolettering(data) {
  useMounted(function () {
    // this is the width that we stored in data from the previous hook
    if (data.data.width > 1200) {
      // we can use refs if they are called in the useMounted hook
      const logoname = this.$refs.logoname;
      Splitting({ target: logoname, by: "chars" });

      TweenMax.staggerFromTo(".char", 5,
        {
          opacity: 0,
          transformOrigin: "50% 50% -30px",
          cycle: {
            color: ["red", "purple", "teal"],
            rotationY(i) {
              return i * 50
            }
          }
        },
        ...

Then, in the component itself, we'll pass one into the other:

<script>
import { logolettering } from "./../hooks/logolettering.js";
import { windowwidth } from "./../hooks/windowwidth.js";

export default {
  hooks() {
    logolettering(windowwidth());
  }
};
</script>

Now we can compose logic with Hooks throughout our application! Again, this is a contrived example for the purposes of demonstration, but you can see how useful this might be for large scale applications to keep things in smaller, reusable functions.

Future plans

Vue Hooks are already available to use today with Vue 2.x, but are still experimental. We’re planning on integrating Hooks into Vue 3, but will likely deviate from React’s API in our own implementation. We find React Hooks to be very inspiring and are thinking about how to introduce its benefits to Vue developers. We want to do it in a way that complements Vue's idiomatic usage, so there's still a lot of experimentation to do.

You can get started by checking out the repo here. Hooks will likely become a replacement for mixins, so although the feature still in its early stages, it’s probably a concept that would be beneficial to explore in the meantime.

(Sincere thanks to Evan You and Dan Abramov for proofing this article.)

The post What Hooks Mean for Vue appeared first on CSS-Tricks.

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.

React Hooks: What’s Going to Happen to Render Props?

About a year ago, I published “How to give rendering control to users with prop getters”. In that post, I showed the entire implementation (at the time) of react-toggled which I actually built for the sole purpose of teaching some of the patterns that I used in downshift. It’s a much smaller and simpler component that implements many of the same patterns as downshift so it served as a great way to teach the prop getters pattern.

Both react-toggled and downshift use the render prop pattern as a mechanism for React component logic code sharing. As I explained in my blog post “When to NOT use Render Props”, that’s the primary use case for the render prop pattern. But that’s also the primary use case for React Hooks as well. And React Hooks are WAY simpler than class components + render props.