# twb

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

Latest version **1.0.1** (published 2024-03-01) · MIT license · 0 weekly downloads

## Install

```sh
npm install twb
pnpm add twb
yarn add twb
bun add twb
```

## Health

**Score 30/100 (F)** — status: abandoned.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.1 |
| Published | 2024-03-01 |
| First published | 2022-03-18 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 62.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | andrienko |
| Maintainers | andrienko |
| Keywords | react, that wombat, tool-belt |

## Links

- npm: https://www.npmjs.com/package/twb
- Repository: https://github.com/andrienko/twb
- Homepage: https://github.com/andrienko/twb#readme
- Issues: https://github.com/andrienko/twb/issues
- npm.io page: https://npm.io/package/twb

## Alternatives

- [mobx-react](https://npm.io/package/mobx-react.md) — 2.8M weekly downloads
- [rc-tree](https://npm.io/package/rc-tree.md) — 2.6M weekly downloads
- [@react-oauth/google](https://npm.io/package/@react-oauth/google.md) — 1.3M weekly downloads
- [@wagmi/connectors](https://npm.io/package/@wagmi/connectors.md) — 877.0K weekly downloads
- [vee-validate](https://npm.io/package/vee-validate.md) — 836.4K weekly downloads

## Recent versions

- 1.0.1 (latest) — 2024-03-01
- 0.3.0 — 2022-05-05
- 0.2.2 — 2022-04-18
- 0.2.1 — 2022-04-08
- 0.1.2 — 2022-03-30
- 0.1.1 — 2022-03-30
- 0.1.0 — 2022-03-30
- 0.0.2 — 2022-03-18
- 0.0.1 — 2022-03-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](https://www.npmjs.com/package/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.

```typeScript
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](https://www.npmjs.com/package/domready). It should also work with
iframes, just like the original.

```typeScript
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`

```typeScript
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.

```typeScript
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.

```typeScript
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:

```typeScript
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:

```typeScript
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.

```typeScript
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.

```typeScript
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.

---
_Source: https://npm.io/package/twb · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
