# use-callback-maa

> better useCallback with helpers

Latest version **0.0.11** (published 2021-05-27) · MIT license · 0 weekly downloads

## Install

```sh
npm install use-callback-maa
pnpm add use-callback-maa
yarn add use-callback-maa
bun add use-callback-maa
```

## Health

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

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

Warnings: low downloads; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.0.11 |
| Published | 2021-05-27 |
| First published | 2021-05-26 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=10 |
| Dependencies | 0 |
| Unpacked size | 64.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | maa105 |
| Maintainers | maa105 |
| Keywords | useCallback, useFixedCallback, useConstantCallback, useImmutableCallback, constant callback, fixed callback, immutable callback, react, callback, hooks, react hooks, callback array |

## Links

- npm: https://www.npmjs.com/package/use-callback-maa
- Repository: https://github.com/maa105/use-callback
- Issues: https://github.com/maa105/use-callback/issues
- npm.io page: https://npm.io/package/use-callback-maa

## 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

- 0.0.11 (latest) — 2021-05-27
- 0.0.10 — 2021-05-27
- 0.0.9 — 2021-05-27
- 0.0.7 — 2021-05-27
- 0.0.6 — 2021-05-27
- 0.0.5 — 2021-05-27
- 0.0.4 — 2021-05-26
- 0.0.3 — 2021-05-26
- 0.0.2 — 2021-05-26
- 0.0.1 — 2021-05-26

## README

# use-callback-maa

> better than useCallback that does not change its identity, with array callback mapper to get singular callbacks to array items

## Install

```bash
npm install --save use-callback-maa
```

## Demo

[See demo](https://maa105.github.io/use-callback/)

## Usage

Below is all the hooks provided. Personally I use `useFixedCallback` and `useFixedCallbackMapper` the most.

The binded ones I use when the handler is not in the scope of the component and thus I need to send it the latest version of some select props.

The dynamic ones are an API idea I had that avoids creating new function on every render (optimization? needs profiling). The dynamic ones are nessessarily binded since the handler is not recreated on each render thus it can not see the latest props, therfore you need to supply the props to the handler by binding them (see below example).

### useFixedCallback(handler)

```tsx
import React, { useState } from 'react'

import { useFixedCallback } from 'use-callback-maa'

// React.memo in conjunction with useFixedCallback will prevent <Child/> from rerendering on click
const Child = React.memo(({ onClick, label }) => {
  return <button onClick={onClick}>{label}</button>
})

const Parent = () => {
  const [counter, setCounter] = useState(0)
  const onClick = useFixedCallback((e: React.MouseEvent) => {
    e.stopPropagation()
    setCounter(counter + 1)
  })
  return (
    <div>
      <div>Counter: {counter}</div>
      <Child onClick={onClick} label='Click Me' />
    </div>
  )
}

const App = () => {
  return <Parent />
}
```

### useFixedCallbackMapper(handler, keyGetter: string | KeyGetter)

This method creates a mapper function that can be then used to `map` an array and provide each item in that array with a fixed identity callback. Note each item's identity is obtained using the keyGetter (which is kindof like lodash's iteratee)

Note the keyGetter is only set once on first render so please keep it constant throughout

```tsx
import React, { useState } from 'react'

import { useFixedCallbackMapper } from 'use-callback-maa'

// React.memo in conjunction with useFixedCallbackMapper will prevent <Child/> from rerendering on click
const Child = React.memo(({ onClick, label }) => {
  const renderCountRef = useRef(0)
  ++renderCountRef.current
  return (
    <button onClick={onClick}>
      {label}
      <br />
      RenderCount:{renderCountRef.current}
    </button>
  )
})

const Parent = () => {
  const [counter, setCounter] = useState(0)
  const [items, setItems] = useState(() => [
    { id: '1', label: 'Item 1' },
    { id: '2', label: 'Item 2' },
    { id: '3', label: 'Item 3' }
  ])

  const mapper = useFixedCallbackMapper((item, e) => {
    e.stopPropagation()
    setCounter(counter + 1)
    if (counter % 6 === 1) {
      setItems(
        items.map((itm) => {
          return itm.id === item.id
            ? {
                ...itm,
                label: `${itm.label.split('[')[0]}[Updated@${counter + 1}]`
              }
            : itm
        })
      )
    }
    if (counter % 6 === 3) {
      setItems([
        ...items,
        {
          id: `${parseFloat(items[items.length - 1].id) + 1}`,
          label: `Item ${parseFloat(items[items.length - 1].id) + 1}[NEW]`
        }
      ])
    }
    if (counter % 6 === 5) {
      setItems(items.filter(({ id }) => id !== item.id))
    }
  }, 'id')

  return (
    <div>
      <b>Every second click will do something</b>
      <div>Counter: {counter}</div>
      {mapper(items, (key, callback, item, i, items) => (
        <Child key={key} onClick={callback} label={item.label} />
      ))}
    </div>
  )
}

const App = () => {
  return <Parent />
}
```

### useBindedCallback(handler, ...bindArgs)

```tsx
import React, { useState } from 'react'

import { useBindedCallback } from 'use-callback-maa'

// React.memo in conjunction with useBindedCallback will prevent <Child/> from rerendering on click
const Child = React.memo(({ onClick, label }) => {
  return <button onClick={onClick}>{label}</button>
})

const counterSetter = (setCounter, counter) => setCounter(counter + 1)
const Parent = () => {
  const [counter, setCounter] = useState(0)

  // This way the handler will not be recreated on each render
  const onClick = useBindedCallback(counterSetter, setCounter, counter)

  // const onClick = useBindedCallback(
  //   (setCounter, counter, e) => {
  //     e.stopPropagation()
  //     setCounter(counter + 1)
  //   },
  //   setCounter,
  //   counter
  // )

  return (
    <div>
      <div>Counter: {counter}</div>
      <Child onClick={onClick} label='Click Me' />
    </div>
  )
}

const App = () => {
  return <Parent />
}
```

### useDynamicBindedCallback(...bindArgs)

The hooks with dynamic in their name use a new API to avoid re creating the handler on every render. But since the handler is created once the dependencies must be binded (using bindArgs).

```tsx
import React, { useState } from 'react'

import { useDynamicBindedCallback } from 'use-callback-maa'

// React.memo in conjunction with useDynamicBindedCallback will prevent <Child/> from rerendering on click
const Child = React.memo(({ onClick, label }) => {
  return <button onClick={onClick}>{label}</button>
})

const Parent = () => {
  const [counter, setCounter] = useState(0)
  const onClick = useDynamicBindedCallback(setCounter, counter)
  onClick.setHandler?.((setCounter, counter, e) => {
    e.stopPropagation()
    setCounter(counter + 1)
  })
  // setHandler will set the callback handler and then remove itself from the callback
  // so come the second render setHandler will be undefined. Hence optional chaining will
  // not call it and a new handler will not be needlessly created

  return (
    <div>
      <div>
        Counter: {counter}
        {onClick.setHandler ? 'WITH setHandler???' : ''}
      </div>
      <Child onClick={onClick} label='Click Me' />
    </div>
  )
}

const App = () => {
  return <Parent />
}
```

### useBindedCallbackMapper(handler, keyGetter: string | KeyGetter, ...bindArgs)

Note the keyGetter is only set once on first render so please keep it constant throughout

```tsx
import React, { useState } from 'react'

import { useBindedCallbackMapper } from 'use-callback-maa'

// React.memo in conjunction with useBindedCallbackMapper will prevent <Child/> from rerendering on click
const Child = React.memo(({ onClick, label }) => {
  const renderCountRef = useRef(0)
  ++renderCountRef.current
  return (
    <button onClick={onClick}>
      {label}
      <br />
      RenderCount:{renderCountRef.current}
    </button>
  )
})

const Parent = () => {
  const [counter, setCounter] = useState(0)
  const [items, setItems] = useState(() => [
    { id: '1', label: 'Item 1' },
    { id: '2', label: 'Item 2' },
    { id: '3', label: 'Item 3' }
  ])

  const mapper = useBindedCallbackMapper(
    (item, /*, binded, args, will, show, here, */ e) => {
      e.stopPropagation()
      setCounter(counter + 1)
      if (counter % 6 === 1) {
        setItems(
          items.map((itm) => {
            return itm.id === item.id
              ? {
                  ...itm,
                  label: `${itm.label.split('[')[0]}[Updated@${counter + 1}]`
                }
              : itm
          })
        )
      }
      if (counter % 6 === 3) {
        setItems([
          ...items,
          {
            id: `${parseFloat(items[items.length - 1].id) + 1}`,
            label: `Item ${parseFloat(items[items.length - 1].id) + 1}[NEW]`
          }
        ])
      }
      if (counter % 6 === 5) {
        setItems(items.filter(({ id }) => id !== item.id))
      }
    },
    'id' /*, binded, args, comes, here */
  )

  return (
    <div>
      <b>Every second click will do something</b>
      <div>Counter: {counter}</div>
      {mapper(items, (key, callback, item, i, items) => (
        <Child key={key} onClick={callback} label={item.label} />
      ))}
    </div>
  )
}

const App = () => {
  return <Parent />
}
```

### useDynamicBindedCallbackMapper(...bindArgs)

```tsx
import React, { useState } from 'react'

import { useDynamicBindedCallbackMapper } from 'use-callback-maa'

// React.memo in conjunction with useDynamicBindedCallbackMapper will prevent <Child/> from rerendering on click
const Child = React.memo(({ onClick, label }) => {
  const renderCountRef = useRef(0)
  ++renderCountRef.current
  return (
    <button onClick={onClick}>
      {label}
      <br />
      RenderCount:{renderCountRef.current}
    </button>
  )
})

const Parent = () => {
  const [counter, setCounter] = useState(0)
  const [items, setItems] = useState(() => [
    { id: '1', label: 'Item 1' },
    { id: '2', label: 'Item 2' },
    { id: '3', label: 'Item 3' }
  ])

  const mapper = useDynamicBindedCallbackMapper(counter, items) // setCounter and setItems have fixed identities no need to bind them
  mapper.setKeyGetter?.('id')
  mapper.setHandler?.((item, counter, items, e) => {
    e.stopPropagation()
    setCounter(counter + 1)
    if (counter % 6 === 1) {
      setItems(
        items.map((itm) => {
          return itm.id === item.id
            ? {
                ...itm,
                label: `${itm.label.split('[')[0]}[Updated@${counter + 1}]`
              }
            : itm
        })
      )
    }
    if (counter % 6 === 3) {
      setItems([
        ...items,
        {
          id: `${parseFloat(items[items.length - 1].id) + 1}`,
          label: `Item ${parseFloat(items[items.length - 1].id) + 1}[NEW]`
        }
      ])
    }
    if (counter % 6 === 5) {
      setItems(items.filter(({ id }) => id !== item.id))
    }
  })

  return (
    <div>
      <b>Every second click will do something</b>
      <div>Counter: {counter}</div>
      {mapper(items, (key, callback, item, i, items) => (
        <Child key={key} onClick={callback} label={item.label} />
      ))}
    </div>
  )
}

const App = () => {
  return <Parent />
}
```

## License

MIT © [maa105](https://github.com/maa105)

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