npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

twb

v1.0.1

Published

A tiny (personal-ish) tool-belt for react apps

Downloads

18

Readme

TWB - My own React tool belt

TWB stands for "That WomBat". It is just a set of snippets and libs I use in most of my projects, to avoid copy-pasting these and just have a single dependency for those. It is written for TypeScript and React (may be used without those, probably, but I don't care too much about such usage). I use it mostly when I prototype things.

I am trying to keep these robust (cuz I use these myself). Feel free to use these too, they won't change much, and if they will - the major version will change. There is no specific goal or anything, not trying to be an "all-in-one-everything" type of thing.

This is intended to be used with react>=18

Features

  • It has React and React-dom as peer dependencies. what will your bundler do with it, and honestly I don't care
  • I target only modern browsers. Probably it would work in IE11 even, but I did not test. Polyfills should work fine, too. I don't use too modern methods, so probably it'd be fine.
  • There are unit tests only for stuff it makes sense for. Hooks are not tested.

Methods

This is not the documentation :P

classNames

I use these for react mostly, but these are agnostic. Alternatives are classNames+@types/classNames and clsx packages.

It is not the best-performant one (I guess): I am using arrays for it, and also check for duplicate classes and trim their names. Perhaps, check for clsx library if it doesn't suit you.

A component from following example will always have a class name of 'test', will have its className prop added, and will have active class name added when isToggled is true.

import React from 'react';
import { classNames } from 'twb';

type Props = {
  className: string,
}

export const Component: React.FC<Props> = ({ className }) => {
  const [isOn, setIsOn] = React.useState<boolean>(false);
  const toggleIsOn = React.useCallback(() => setIsOn(s => !s), []);
  return (
    <button
      className={classNames('test', className, {'active': isToggled})}
      onClick={toggleIsOn}
    >
      Click me!
    </button>
  );
}

domReady

It is a function to fire when DOMContentLoaded has been fired. It is similar to popular domready+@types/domready package. Probably you are looking for the original. It should also work with iframes, just like the original.

import { domReady } from 'twb';

domReady(() => {
  const helloDiv = document.createElement('div');
  helloDiv.innerHTML = 'Hello, world!';
  document.body.appendChild(helloDiv);
});

Nowadays, the common approach to JS is to simply add scripts right before body tag. I believe a good script should work no matter where, when and how it was added.

mountReact and mountRoot

I don't like the common approach of having a div with an id in document body and the finding it - I prefer to create the wrapper myself.

I often have all of my react code fit into component. And then there is a "loader" script or something that handles creating a wrapper div (appended to document.body), creating react component and passing it into reactDom.render with wrapper as target. That is basically what the function does. The second, optional argument is className for that wrapper div.

By default, that wrapper will be created with #main id. This is useful when you have HMR enabled - the functions check if an element with specified ID exists - and mounts to it if it does.

These functions save the hassle typing React.render and React.createElement

import { mountReact } from 'twb';
import { App } from './App';
mountReact(App);

there is also a mountRoot function, that does the same, but uses the new createRoot API

noop

Noop is noop. A function that does nothing. I often need it.

randomId

Returns a string ID. Basically it is a Math.random().toString(16).slice(2) with an option to set (any) length of that id. Also there is a nextId function that when called returns an incremental id (hexadecimal from 0) and a fakeUuid method that returns a string in format `00000000-0000-0000-0000-000000000000' (it is NOT a real, RFC4122-compliant UUID, but just a random string that LOOKS like UUID)

Second argument of randomId is radix - by default it is 16 for hexadecimals.

useState

A wrapper around React.useState hook that returns an object with value and set fields (instead of an array of those). I use it for contexts when I'm lazy to use reducer.

import React from 'react';
import { useState, useTextChangeHandler } from 'twb';

export const App:React.FC = () => {

  // The point is there's single variable here
  const textState = useState<string>('');
  const handleTextChange = useTextChangeHandler(textState.set);

  return <input
    type="text"
    value={textState.value}
    onChange={handleTextChange}
  />
}

useUpdatedValues

Similar to useWhyDidYouUpdate that is often used to debug which values were changed to prevent excess renders and stuff while working with react hooks. It accepts an array of values and console.logs those that have been changed.

import React from 'react';
import { useUpdatedValues } from 'twb';

export const App:React.FC = () => {

  const [text, setText] = React.useState<string>('');
  const handleTextChange = useTextChangeHandler(textState.set);

  // First argument is an object of labels and values to check if they were
  // changed. Second argument is label, helpful when you are trying to track
  // reason for re-render of nested component
  useUpdatedValues({ text }, 'App');

  return <input type="text" value={text} onChange={handleTextChange} />
}

useTextChangeHandler

I often need to write something like this:

const [text, setText] = React.useState<string>('');
const handleTextChange = React.useCallback(
  (e: React.ChangeEvent<HTMLInputElement>) => setText(e.currentTarget.value),
  []
);

The hook basically does that. It's just this now:

const [text, setText] = React.useState<string>('');
const handleTextChange = useTextChangeHandler(setText);

useToggle

Similar to handleTextChange, but to work with boolean states. For react state setter it will return a function that will set value to the opposite.

useStableCallback

Similar to react's useCallback, but there is no second (dependencies) argument and the function the hook returns is always stable (it simply keeps the function you pass to the hook in a ref and calls it, the hook updates the function every time a new function is passed)

You can also use the callback with useEffect to only react to what you want to react (react's eslint rules require you to specify variables that are used inside useEffect as dependencies, and you don't always need to react to those)

createContext

A function that takes a hook function that should return object context value as an argument - and returns a hook and context provider. The returned hook will throw if used outside provider.

import React from 'react';
import { createContext } from 'twb';

const useHookValue = () => {
  const [text, setText] = React.useState<string>('');
  return React.useMemo(() => ({text, setText}), [text]);
}

export const [useTextContext, TextContextProvider] = createContext(useHookValue);

It will derive the hook value from return of that useHookValue (but you can provide it manually). If you name the fn as a hook (useWhatever) react eslint rules will help you.

import React from 'react';
import { useContext } from './WhateverContext';
import { useTextChangeHandler } from 'twb';

export const Child = () => {
  const { text, setText } = useContext();
  const handleValueChange = useTextChangeHandler(setText);
  return <input value={text} onChange={handleValueChange} />;
};

stopPropagation, preventDefault and stopPrevent

Functions that call preventDefault, stopPropagation and both of these respectively on passed React.SyntheticEvent

types

There are several type helpers also.

  • FCC is an old React.FC (FunctionComponent + children)
  • ReactComponent is anything that passes as react component (first argument of React.createElement)
  • StringRecord is Record<string, string> - an object of strings
  • UnknownRecord is Record<string, unknown> - any object

Building

npm run build

Uses microbundle to build. Haven't found an option to disable emitting declarations (cuz I write it manually) but found a way to broke it - so build will spit several errors about declarationDir - disregard these :)

Uses jest and ts-jest to test, but there's not much to test.