@s-ui/react-hooks

1.38.0 • Public • Published

ReactHooks

Useful hooks to be used in your React projects

Requirements

Hooks where a new addition in React 16.8. They let you use state and other React features without writing a class.

react >=16.8

Installation

$ npm install @s-ui/react-hooks --save

Available hooks

useMount

Hook that will be executed when the component mounts. Similar behaviour to old componentDidMount but useMount could return a function that will executed when the component unmounts. Useful for clearing events or timers.

import useMount from '@s-ui/react-hooks/lib/useMount/index.js'

export default () => {
  useMount(() => {
    window.analytics.track('component did mount!')
  })

  useMount(() => {
    const onScroll = () => console.log('scrolling')
    document.addEventListener('scroll', onScroll)
    return () => document.removeEventListener('scroll', onScroll)
  })

  const [currentUser, setCurrentUser] = useState(null)

  useMount(async () => {
    const user = await getUser()
    setCurrentUser(user)
  })

  return currentUser && <h1>Hello {currentUser.name} !!</h1>
}

useOnScreen

Hook to detect if an element is on the screen. Useful for lazy loading components, images or fetching of data.

You could configure if it should only be fired once by using the parameter with the same name (default: true).

import useOnScreen from '@s-ui/react-hooks/lib/useOnScreen/index.js'

export default () => {
  const [isIntersecting, outerRef] = useOnScreen({ once: true })
  const [isIntersectingBlock, outerRefBlock] = useOnScreen({ once: false })

  return (
    <div ref={outerRef}>
      {isIntersecting && <img src='huge-image.png' />}
      <div>
    </div>
  )
}

useOrientation

This Hook listens for orientation changes and returns current orientation information Returns: {orientation, side}

import useOrientation, {orientations} from '@s-ui/react-hooks/lib/useOrientation/index.js'

export default () => {
  const {orientation, side} = useOrientation()

  return (
    <>
      {orientation === orientations.portrait && <p>portrait</p>}
      {orientation === orientations.landscape && <p>landscape - {side}</p>}
    </>
  )
}

useNearScreen

Similar to useOnScreen but it let you configure when the distance is enough to return true. By default if the element is 200px near the screen it will change the inner state of the hook. You could define the offset in pixels to fire the event sooner or later.

import {useNearScreen} from '@s-ui/react-hooks/lib/useOnScreen/index.js'

export default () => {
  const [isNear, outerRef] = useNearScreen({ offset: '300px' })

  return (
    <div ref={outerRef}>
      {isNear && <p>This is 300px near to the viewport!</p>}
      <div>
    </div>
  )
}

useLegacyState

Hook that provides a similar state management approach to the old this.setState from a class.

Useful to cover quick functional migrations in components with complex states that would cause unnecessary renders if simply divided into lots of useState hooks.

If you apply this when migrating to a functional component, please take in mind that you may later rethink the strategy of its state.

import useLegacyState from '@s-ui/react-hooks/lib/useLegacyState/index.js'

export default () => {
  const initialState = {
    availableFood: 20,
    catsFood: 0,
    dogsFood: 0
  }
  const [state, setState] = useLegacyState(initialState)
  const {availableFood, catsFood, dogsFood} = state

  const giveCatsFood = () => {
    if (availableFood >= 2)
      setState({
        availableFood: availableFood - 2,
        catsFood: catsFood + 2
      })
  }
  const giveDogsFood = () => {
    if (availableFood >= 2)
      setState({
        availableFood: availableFood - 2,
        dogsFood: dogsFood + 2
      })
  }
  const giveFoodToAll = () => {
    if (availableFood >= 2)
      setState({
        availableFood: availableFood - 2,
        catsFood: catsFood + 1,
        dogsFood: dogsFood + 1
      })
  }

  return (
    <div>
      <header>Available food: {availableFood || 'No more!'}</header>
      <button onClick={giveCatsFood}>Give cats food</button>
      <button onClick={giveDogsFood}>Give dogs food</button>
      <button onClick={giveFoodToAll}>Give food to all</button>
      <ul>
        <li>Cats had {catsFood} of food.</li>
        <li>Dogs had {dogsFood} of food.</li>
      </ul>
    </div>
  )
}

useMediaQuery

This is a CSS media query hook for React. It listens for matches to a CSS media query. It allows the rendering of components based on whether the query matches or not.

Hook useMediaQuery always returns a boolean and it indicates if query matches or not.

import useMediaQuery from '@s-ui/react-hooks/lib/useMediaQuery/index.js'

export default function Demo() {
  const isMatching = useMediaQuery('(min-width:600px)')

  return <span>{`(min-width:600px) matches: ${isMatching}`}</span>
}

On the server, by default, the mediaQuery doesn't match but you could pass an option to change this.

import useMediaQuery from '@s-ui/react-hooks/lib/useMediaQuery/index.js'

export default function Demo() {
  const isMatching = useMediaQuery('(min-width:600px)', {defaultMatches: true})
  return <span>{`(min-width:600px) matches: ${isMatching}`}</span>
}

Keep in mind this functionality if you want to prioritize one design over other. For example, to be sure you render on the server the mobile layout and keep for the client.

useScroll

Hook to get the scroll position and the direction of scroll, limited to the Y axis.

The hook useScroll always returns an object with position and direction of scroll

import useScroll from '@s-ui/react-hooks/lib/useScroll/index.js'

export default function Demo() {
  const {position, direction} = useScroll()

  return (
    <>
      <p>{`Scroll position: ${position}`}</p>
      <p>{`Scroll direction: ${direction}`}</p>
    </>
  )
}

useDebounce

Hook to debounce any fast changing value.

The hook useDebounce always returns a given value after some miliseconds.

import useDebounce from '@s-ui/react-hooks/lib/useDebounce/index.js'

export default function Demo() {
  const [searchTerm, setSearchTerm] = useState('')
  const debouncedSearchTerm = useDebounce(searchTerm, 500)

  useEffect(() => {
    if (debouncedSearchTerm) {
      //do something with the debounced value
    }
  }, [debouncedSearchTerm])

  return (
    <input
      placeholder="Search anything"
      onChange={e => setSearchTerm(e.target.value)}
    />
  )
}

useOnClickOutside

Hook to call to action when click outside a element.

The hook useOnClickOutside allows you to detect clicks outside of a specified element. In the example below we use it to close a modal when any element outside of the modal is clicked. By abstracting this logic out into a hook we can easily use it across all of our components that need this kind of functionality (dropdown menus, tooltips, etc).

This hook is copied from useHooks

import {useRef, useState} from 'react'
import useOnClickOutside from '@s-ui/react-hooks/lib/useOnClickOutside/index.js'

export default function Demo() {
  // Create a ref that we add to the element for which we want to detect outside clicks
  const ref = useRef()
  // State for our modal
  const [isModalOpen, setModalOpen] = useState(false)
  // Call hook passing in the ref and a function to call on outside click
  useOnClickOutside(ref, () => setModalOpen(false))

  return (
    <div>
      {isModalOpen ? (
        <div ref={ref}>
          👋 Hey, I'm a modal. Click anywhere outside of me to close.
        </div>
      ) : (
        <button onClick={() => setModalOpen(true)}>Open Modal</button>
      )}
    </div>
  )
}

useEventListener

The useEventListener hook is useful to avoid a lot of boilerplate when setting one or more event listeners in a component.

Usage

Params

It accept 4 arguments, the first 2 mandatory, the others are optionals.

  • event type<String|Array<String>>: It could be a single string representing an event or more passed with an array [required]

  • handler<Function>: The function handler to invoke when the event occurs [required]

  • target<HtmlElement>: The target of our event listeners [default: Client: window | Server: null]

  • options<Object>: Optional options to pass to the event listener [default: {}]

import useEventListener from '@s-ui/react-hooks/lib/useEventListener/index.js'

function Component() {
  const divRef = useRef()

  useEventListener('scroll', () => console.log('Scrolled the whole document'))
  useEventListener(
    ['click', 'touchstart'],
    () => console.log('Div clicked or touched!'),
    divRef.current
  )

  return <div ref={divRef} />
}

useInterval

The useInterval hook is useful to create intervals conditionally.

Usage

Params

It accepts 2 arguments.

  • callback>: The function to invoke [required]

  • delay: The delay time in milliseconds [default: 0]

function Component() {
  const [count, setCount] = React.useState(0)
  const [delay, setDelay] = React.useState(1000)
  const [isRunning, toggleIsRunning] = useBoolean(true)

  useInterval(
    () => {
      setCount(count + 1)
    },
    isRunning ? delay : null
  )

  return (
    <div>
      <div>
        delay:{' '}
        <input
          value={delay}
          onChange={event => setDelay(Number(event.target.value))}
        />
      </div>
      <h1>count: {count}</h1>
      <div>
        <button onClick={toggleIsRunning}>
          {isRunning ? 'stop' : 'start'}
        </button>
      </div>
    </div>
  )
}

useBoolean

The useBoolean hook is particularly useful when is necessary to trigger conditional renders in consequence of events. A typical use case is to handle a modal show/hide logic. It is an extension of the useToggle hook.

Usage

Params

It accepts a single boolean argument which is used as initial value.

  • initialValue [default: false]

Return value: Array<Boolean, Object>

  • value
  • handlers
    • toggle: Toggle the current value
    • on: Set to true the value
    • off: Set to false the value
import useBoolean from '@s-ui/react-hooks/lib/useBoolean/index.js'

function Component() {
  const [value, {toggle, on, off}] = useBoolean()

  return (
    <div>
      <span>{value.toString()}</span>
      <button onClick={toggle}>Toggle!</button>
      <button onClick={on}>Turn true!</button>
      <button onClick={off}>Turn false!</button>
    </div>
  )
}

useToggle

The useToggle hook abstracts the often-used logic to toggle a boolean state. It is especially useful when handling checkboxes and on/off conditional renders, but it could be used in composition with additional logic to create more powerful hooks (useBoolean, ...)

Usage

import useToogle from '@s-ui/react-hooks/lib/useToggle/index.js'

function Component() {
  const [value, toggle] = useToggle()

  return (
    <div>
      <span>{value.toString()}</span>
      <button onClick={toggle}>Toggle!</button>
    </div>
  )
}

useIsomorphicLayoutEffect

An alternative to useLayoutEffect that does not show warning when server-side rendering

Usage

import useIsomorphicLayoutEffect from '@s-ui/react-hooks/lib/useIsomorphicLayoutEffect/index.js'

function Component({value}) {
  useIsomorphicLayoutEffect(() => {
    window.console.log(value)
  }, [value])

  return null
}

useCallbackRef

Persist a function between renders but keeps it up-to-date if it changes (commonly known as The Latest Ref Pattern).

Usage

import useCallbackRef from '@s-ui/react-hooks/lib/useCallbackRef/index.js'

function Component({onChange: onChangeProp}) {
  const onChange = useCallbackRef(onChangeProp)

  useEffect(() => {
    window.library.init({
      id: '#id',
      onChange
    })
  }, [onChange])

  return <div id="id" />
}

useSteps

The useSteps hook allow to create a steps handler for navigating between steps.

Usage

Params

It accepts a single argument which is used as initial value. If not passed, the history will be empty in the beginning

  • initialValue: required

Return value: Object

  • step: any The current step
  • history: Array<any> The steps history list
  • lastAction: next|prev|reset The last triggered action type
  • next: (nextStep) => void Set the next step passed as argument
  • prev: () => void Bring back to the previous step. If there are less than the
  • reset: () => void Set to false the value
  • isActive: (step) => boolean Check if the passed step is marked as the current
import useSteps from '@s-ui/react-hooks/lib/useSteps/index.js'

function Component() {
  const {next, prev, step, history, lastAction, reset} = useSteps(0)

  return (
    <>
      {prev && <Button onClick={prev}>Prev</Button>}
      <Strong>Step: {steps[step]}</Strong>
      <Button onClick={() => next(step + 1)}>Next</Button>
      <Strong>History: {history.toString()}</Strong>
      <Strong>Last action: {lastAction}</Strong>
      <Button onClick={reset}>Reset</Button>
    </>
  )
}

useMergeRefs

The useMergeRefs hook has the purpose of merging and sync the passed references into a single one. It could be useful when implementing a component that receives a forwarded ref and that use internal refs, making it easier to pass a single ref to the DOM target.

Usage

Params

It accepts a list of ref arguments.

Return value: Ref<(value) => void>

import useMergeRefs from '@s-ui/react-hooks/lib/useMergeRefs/index.js'

const Component = React.forwardRef((props, forwardedRef) => {
  const firstRef = useRef()
  const secondRef = useRef()

  const ref = useMergeRefs(firstRef, secondRef, forwardedRef)

  return <div ref={ref} />
})

Find full description and more examples in the demo page.

useControlledState

The useControlledState hook has the purpose of combine the state of a value prop and its default value.

import useControlledState from '@s-ui/react-hooks/lib/useControlledState/index.js'

const Component = ({value, initialValue, onClick, onReset}) => {
  const [
    innerValue, // state
    setInnerValue, // state setter
    isControlledValue, // {boolean} using controlled behavior or not
    initialInnerValue // first inner value state
  ] = useControlledState(value, initialValue)
  const onHandler = (handler, value) => () => {
    setInnerValue(value)
    typeof handler === 'function' && handler(value)
  }
  return (
    <FooBar
      value={innerValue}
      onClick={onHandler(onClick, innerValue)}
      onReset={onHandler(onReset, initialInnerValue)}
    />
  )
}

useMountedState

The useMountedState hook provides the ability to check component's mount state. Returns a function that will return true if component mounted and false otherwise.

import useMountedState from '@s-ui/react-hooks/lib/useMountedState/index.js'

const Demo = () => {
  const isMounted = useMountedState()

  React.useEffect(() => {
    setTimeout(() => {
      if (isMounted()) {
        // ...
      } else {
        // ...
      }
    }, 1000)
  })
}

useCopyToClipboard

Copy text to a user's clipboard.

import useCopyToClipboard from '@s-ui/react-hooks/lib/useCopyToClipboard/index.js'

const Demo = () => {
  const [text, setText] = React.useState('')
  const [{value, error}, copyToClipboard] = useCopyToClipboard()

  return (
    <div>
      <input value={text} onChange={e => setText(e.target.value)} />
      <button type="button" onClick={() => copyToClipboard(text)}>
        copy text
      </button>
      {error ? (
        <p>Unable to copy value: {error.message}</p>
      ) : (
        value && <p>Copied {value}</p>
      )}
    </div>
  )
}

Readme

Keywords

none

Package Sidebar

Install

npm i @s-ui/react-hooks

Weekly Downloads

3,144

Version

1.38.0

License

MIT

Unpacked Size

101 kB

Total Files

56

Last publish

Collaborators

  • daniel.perez.ext
  • albert.peiro
  • frandelacasa-adevinta
  • mariapaula.forero.ext
  • hpintos_adevinta
  • sziauberyte
  • victor.perez.adevinta
  • oscar-raig-adevinta
  • carlos.gonzalezl
  • carolina.mallo.ext
  • david.nieto
  • ferran.simon
  • xavi_conejo
  • sergi.quintela
  • jamile.radloff
  • xavi_ballestar
  • luz_adv
  • ignacio.rodriguez
  • carlosvillu-adevinta
  • diegomr
  • arturo.vicente
  • adria.velardos
  • emiliovz
  • dann41
  • ruben-martin
  • pol.valls
  • cristina.rodriguez.duque
  • sergio.escano
  • marc.benito
  • azahara
  • patricio.sartore
  • giovanny.sayas.ext
  • david.cuadrado.ext
  • alex.castells
  • beatrizip
  • pablo.rey-adevinta
  • sergiocollado
  • mango.sanchezredondo
  • cristhianb
  • alisa_bayanova
  • davidmartin2108
  • ferrangbtw
  • estefania_garcia
  • belen.santos
  • alfredo.arronte
  • joanleon-adv
  • giodelabarrera.adevinta
  • luis-garrido
  • aitor.rodriguez
  • jordi.munoz
  • oscar_ramirez
  • ignacio_navarro
  • a.ferrer
  • gfabregoadv
  • izeller
  • salvador.juan
  • oriol.egea
  • ivanmlaborda
  • alejandro.ferrante
  • pa.chruscinski.ext
  • isabelgomez87
  • jenifer.lopez
  • daniela.aguilera
  • pablogs
  • javier.miguel
  • oscar.gomez
  • marian.lucaci
  • alverd004
  • oriol.puig
  • nacho_torrella
  • xavi.murcia
  • chloe.leteinturier
  • javiauso
  • alfredo.zimperz
  • francisco.ruiz.lloret
  • fermin.adevinta
  • andresadv
  • schibstedspain