@upstatement/react-hooks
TypeScript icon, indicating that this package has built-in type declarations

1.0.2 • Public • Published

@upstatement/react-hooks

A collection of Upstatement's most-used React hooks

A collection of Upstatement's most-used React hooks across projects, updated to have first-class TypeScript support!

Table of Contents

Requirements

This package has the following peer dependencies:

  • React v16.8.0+ (for hooks ⚓️)

Installation

With npm:

$ npm install @upstatement/react-hooks

With yarn:

$ yarn add @upstatement/react-hooks

Then with a module bundler like webpack, use as you would anything else:

// using ES6 modules
import { useMultiRef, useSet } from '@upstatement/react-hooks';
import useMultiRef from '@upstatement/react-hooks/dist/esm/useMultiRef';

// using CommonJS modules
const { useMultiRef, useSet } = require('@upstatement/react-hooks');
const useMultiRef = require('@upstatement/react-hooks/dist/cjs/useMultiRef');

👋 Meet the Hooks

useState

This basic hook provides an extra layer of security over React's existing useState hook. While it functions the exact same as the original, our hook will no longer accept updates to the state post-unmount.

Learn more about the useState API and Usage

API

The API remains unchanged from React's useState hook: https://reactjs.org/docs/hooks-reference.html#usestate

Usage

import { useState } from '@upstatement/react-hooks';

const App = () => {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count => count + 1);
  };

  return (
    <div>
      <h4>Count: {count}</h4>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

useStateReducer

This hook is a step between the useState and useReducer hooks, providing a way to create a mini state store inside your component.

Learn more about the useStateReducer API and Usage

API

const [state, set] = useStateReducer(initialState);

The initial state should be a record of key-value pairs. Similar to useState, these values can either be the exact value, or a function to be lazily evaluated when the hook is run. For example:

const [state, set] = useStateReducer({
  name: 'John',
  expensiveValue: () => {
    const initialState = someExpensiveComputation();
    return initialState;
  },
});

The set function contains a number of properties that update the respective values within the state. For example:

const [state, set] = useStateReducer({
  age: 6,
  maxAge: 8,
});

set.age(5); // Sets age to 5
set.age(age => age + 1); // Increases age by 1
set.age((age, state) => Math.min(age + 1, state.maxAge)); // Increases age by 1, capped by current state's maxAge value

Usage

import { useStateReducer } from '@upstatement/react-hooks';

const UserForm = ({ onSubmit }) => {
  const [state, set] = useStateReducer({
    name: '',
    age: 0,
    createdAt: () => new Date(),
  });

  return (
    <form onSubmit={onSubmit}>
      <input id="name" value={state.name} onChange={evt => set.name(evt.target.value)} />
      <input id="age" type="number" value={state.age} onChange={evt => set.age(evt.target.value)} />
    </form>
  );
};

usePrevious

This hook allows for tracking of the value of a given variable on a previous render.

Learn more about the usePrevious API and Usage

API

const previousValue = usePrevious(currentValue);

The initial previous value returned will be the same as the current value.

It's important to note that the previous value does not update when the given value changes, but rather on every render.

Usage

import { usePrevious } from '@upstatement/react-hooks';

const Direction = ({ scrollY }) => {
  const previousScrollY = usePrevious(scrollY);

  if (scrollY === previousScrollY) {
    return null;
  } else if (scrollY > previousScrollY) {
    return <ArrowUp />;
  }
  return <ArrowDown />;
};

useMap

This hook allows for use of the ES6 Map as a state variable inside your React component.

Learn more about the useMap API and Usage

API

const map = useMap(arrayOfTuples);
// Accepts the same initial value that Map's constructor does

All map methods can then be used as normal, including (but not limited to) map.set, map.has, and map.delete.

Usage

import { useMap, useState } from '@upstatement/react-hooks';

const DictionarySearch = () => {
  const dictionaryMap = useMap();

  const [search, setSearch] = useState('');
  const [term, setTerm] = useState('');
  const [definition, setDefinition] = useState('');

  const addDefinition = evt => {
    evt.preventDefault();
    dictionaryMap.add(term, definition);
    setTerm('');
    setDefinition('');
  };

  const onChange = setFunction => evt => {
    setFunction(evt.target.value);
  };

  return (
    <div>
      <input id="search" value={search} onChange={onChange(setSearch)} />
      {dictionaryMap.has(search) ? (
        <p style={{ color: 'green' }}>{dictionaryMap.get(search)}</p>
      ) : (
        <p style={{ color: 'red' }}>Term not found in dictionary.</p>
      )}
      <form onSubmit={addDefinition}>
        <input id="term" value={term} onChange={onChange(setTerm)} />
        <textarea id="definition" value={definition} onChange={onChange(setDefinition)}></textarea>
      </form>
    </div>
  );
};

useSet

Similar to useMap, this hook allows you to use an ES6 Set as a state variable inside your React component.

Learn more about the useSet API and Usage

API

const set = useSet(arrayOfValues);
// Accepts the same initial value that Set's constructor does

All set methods can then be used as normal, including (but not limited to) set.add, set.has, and set.delete.

Usage

import { useSet } from '@upstatement/react-hooks';

const Shop = ({ items }) => {
  const cartSet = useSet();

  const addToCart = index => {
    const item = items[index];
    if (item) {
      cartSet.add(item.name);
    }
  };

  return (
    <div>
      <h2>Items</h2>
      <ul>
        {items.map(({ name, price }, index) => (
          <li key={name}>
            <p>{name}</p>
            <p>${price}</p>
            <button disabled={cartSet.has(name)} onClick={() => addToCart(index)}>
              Add to cart
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
};

useMultiRef

Allows for tracking multiple refs in the React DOM. This is particularly useful when looping over items.

Learn more about the useMultiRef API and Usage

API

const [refs, setRef] = useMultiRef();

Usage

import { useEffect } from 'react';
import { useMultiRef } from '@upstatement/react-hooks';
import { last } from 'lodash';

const Modal = ({ links }) => {
  const [linkRefs, setLinkRef] = useMultiRef();

  const lockModalFocus = evt => {
    if (evt.keyCode === 9) {
      // Pressed tab
      const linkEls = linkRefs.current;
      if (evt.shiftKey && document.activeElement === linkEls[0]) {
        evt.preventDefault();
        last(linkEls).focus();
      } else if (!evt.shiftKey && document.activeElement === last(linkEls)) {
        evt.preventDefault();
        linkEls[0].focus();
      }
    }
  };

  useEffect(() => {
    linkRefs.current[0].focus();
    window.addEventListener('keydown', lockModalFocus);
    return () => {
      window.removeEventListener('keydown', lockModalFocus);
    };
  }, []);

  return (
    <ul>
      {links.map(({ href, text }, index) => (
        <li key={index} ref={setLinkRef(index)}>
          <a href={href}>{text}</a>
        </li>
      ))}
    </ul>
  );
};

useForceUpdate

This utility hook provides a way to force the a component to update. It's recommended to only be used when the DOM is dependent on a ref value.

Learn more about the useForceUpdate API and Usage

API

const update = useForceUpdate();

Every call to the update function will increase an internal tick. This in turn will force a re-render of the component.

Usage

This hook is actually used in our useSet and useMap hooks! A snippet of that code is found below:

import { useRef } from 'react';
import { useForceUpdate } from '@upstatement/react-hooks';

const useSet = iterable => {
  const update = useForceUpdate();
  const setRef = useRef(new Set(iterable));

  const set = new Set(setRef.current);

  set.add = value => {
    const newSet = setRef.add(value); // Add to our set reference
    update(); // force update to hook, recreating the `set` value
    return newSet;
  };

  return set;
};

Contributing

We welcome all contributions to our projects! Filing bugs, feature requests, code changes, docs changes, or anything else you'd like to contribute are all more than welcome! More information about contributing can be found in the contributing guidelines.

Code of Conduct

Upstatement strives to provide a welcoming, inclusive environment for all users. To hold ourselves accountable to that mission, we have a strictly-enforced code of conduct.

About Upstatement

Upstatement is a digital transformation studio headquartered in Boston, MA that imagines and builds exceptional digital experiences. Make sure to check out our services, work, and open positions!

Package Sidebar

Install

npm i @upstatement/react-hooks

Weekly Downloads

0

Version

1.0.2

License

ISC

Unpacked Size

29.5 kB

Total Files

35

Last publish

Collaborators

  • upstatementeng