1.0.0 • Published 4 years ago

use-delayed-function v1.0.0

Weekly downloads
2
License
MIT
Repository
github
Last release
4 years ago

use-delayed-function

React hook to call a function with delay

NPM JavaScript Style Guide

This hook provides a safe way to call a function with delay and it takes care of required cleanups.

It doesn't have state and it won't cause extra rendering, so it is useful for sequential (or asynchronous) tasks where we want chain async functions on top of each others.

Install

npm install --save use-delayed-function

General structure:

  const [delayedFunction, cancelIt] = useDelayedFunction(
    originalFunction,
    delay,
    rejectOnCancel
  )

General usage format:

delayedFunction(para).then(result=>{setState(result)})

Use cases

It can be used for debouncing which simply delays all consecuative attempts to call a function and finally the last attempt runs if it persists enough time.

It is also a handy tool for applying timing logics inside react components e.g. Showing a notification for few seconds.

It is also a good choice to handle initializing stage of a component. Combination of calling functions, async task or even changing states in sequence can be handled through promise chain provided behind the scene. This kind of behaviour is mostly desirable while initializing a component.

Activities of this hook doesn't change state of the component (unless the called function set an state) so it won't cause extra rendering. The stateful version of this hook is use-delayed-state

How to use

To see deployed examples and source codes click here.

The most simplest use case is calling a function in future. In below example changeContentLater is a function that will call changeContent with delay. Note that delay is a prop (it could be a local state as well) and it dynamically controls delay value.

import React, { useRef, useEffect } from 'react'

import useDelayedFunction from 'use-delayed-function'

const SimpleExample = ({ delay = 4000 }) => {
  const divRef = useRef()

  const [changeContentLater] = useDelayedFunction(changeContent, delay)

  function changeContent(target, content) {
    target.innerHTML = content
  }

  useEffect(() => {
    changeContentLater(divRef.current, 'This is the new content')
  }, [])

  return (
    <div>
      <div ref={divRef} className='description'>
        {`This content will change after ${delay}ms from component mounting`}
      </div>
    </div>
  )
}

export default SimpleExample

In debouncing example any debounced changes in textarea appears in another paragraph.

  const [debounceChange] = useDelayedFunction(changeContent, 900)

  const [removeStyleLater] = useDelayedFunction(removeStyle, 1000)

  const [addStyleNow] = useDelayedFunction(addStyle)

  function changeContent(target, content) {
    target.innerHTML = content
    return target
  }

  function addStyle(target) {
    target.className = 'updating'
    return target
  }

  function removeStyle(target) {
    target.className = ''
  }

  function handleChange(e) {
    debounceChange(contentRef.current, e.target.value)
      .then(addStyleNow)
      .then(removeStyleLater)
  }
  return (
    <div>
      <textarea
        type='text'
        placeholder='Type something'
        onChange={handleChange}
      />
      <p ref={contentRef}></p>
    </div>
  )

As you can see in this line of code we can easily manage the timing logic that we want.

    debounceChange(contentRef.current, e.target.value)
      .then(addStyleNow)
      .then(removeStyleLater)

Details

  const [delayedFunction, cancelIt] = useDelayedFunction(
    originalFunction,
    delay,
    rejectOnCancel
  )
  • delayedFunction

    • Is a function wrapped around the originalFunction and always returns a promise.
    • It accepts and passes down arguments to the originalFunction.
    • It will resolve to return value of the originalFunction.
    • If "originalFunction" is an async function the best time for setState or DOM manipulation tasks is where it resolved. delayedFunction(para).then(doSetState)
    • Consecutive calls to this function will cancel previous unfinished calls.
  • cancelIt(doNotReject)

    • Is a function which will cancel any pending call to originalFunction.
    • If it was too late and originalFunction already fired it will break the chain and ignore its return value.
    • Calling this function will cause reject of delayedFunction if rejectOnCancel===true otherwise it leaves the promise in pending state to be removed by garbage collector.
    • If doNotReject==true it won't reject the promise even rejectOnCancel===true
  • originalFunction

    • It can be a regular or async function
    • It can accept arguments and return values
    • setState or DOM manipulation tasks can be done inside this function with no worries if they are synchronous.
    • Any setState or DOM manipulation based on async processes is forbidden inside this function because it throw errors if component was unmounted. For this purpose use this format delayedFunction(para).then(result=>{setState(result)})

  • delay

    • Is the delay before calling the originalFunction
    • Is in milliseconds
    • If not specified considered as 0
    • It could be a useRef object so delay value loaded from refObject.current
    • If it specified by a prop,state or ref, runtime value of delay will change if they change
  • rejectOnCancel

    • Is an optional boolian parameter. If not specified considered as false
    • If rejectOnCancel==true the canceled calls will reject to { message: 'Function call canceled', timestamp: Date.now() }
    • It is useful for tracking canceled calls

License

MIT © makannew