Skip to content

Instantly share code, notes, and snippets.

@jancassio
Last active March 6, 2020 11:26
Show Gist options
  • Save jancassio/bcc701eeac8849b5c686f118d992b5c1 to your computer and use it in GitHub Desktop.
Save jancassio/bcc701eeac8849b5c686f118d992b5c1 to your computer and use it in GitHub Desktop.
React Hooks

React Hooks

useImage

Provides an image from a respective URL

Usage

const Component = ({ src, alt, width = 320, height = 240, fallback }) => {
  const { progress, response, error } = useImage(src)

  if (response) {
    return <img src={response.data} alt={alt} width={width} height={height} />;
  } else if (error) {
    return fallback ? fallback(error) : '❌';
  }

  return (
    <ProgressLoading
      progress={progress.ratio}
      width={`${width}px`}
      height={`${height}px`}
    />
  );
};

useScrollPosition

Allows to get x and y scroll position on the page.

Usage

const Component = () => {
  const scroll = useScrollPosition();
  return (
    <pre>
      <code>x = {scroll.x}</code>
      <br />
      <code>y = {scroll.y}</code>
      <hr />
    </pre>
  );
};

useReflow

Toggle classes on every element update

Usage

const Component = () => {
  const [value, setValue] = useState('')
  
  // useReflow arguments are css classes to be attributed when its own dom changes
  const ref = useReflow('highlight', 'bolder') 
  return (
    <>
      <h1>Value changed to <span ref={ref}>{value}</span></h1>
      <input type="text" value={value} onChange={(event) => setValue(event.target.value)} />
    </>
  )
}

useSearchQueryParams

Shorthand to URL query parameters

Usage

const Component = () => {
  const query = useSearchQueryParams()
  const page = query.get('page')
  const limit = queryu.get('limit')
  
  return (
    <p>This page is = {page} and the limit items by page is {limit}</p>
  )
}

useInputFormated

Helps to format masked or patterns on input fields

Usage

import React, { createRef } from "react";
import useInputFormated from "../hooks/useInputFormated";

const toCurrency = value =>
  value
    .toLocaleString("pt-BR", {
      style: "currency",
      currency: "BRL"
    })

export default function CurrencyField() {
  const ref = createRef()
  const [value] = useInputFormated({ ref, formatter: toCurrency })
  return <input ref={ref} type="text" value={value} />;
}
import { useEffect, useState } from 'react';
const loadImage = (src, onProgress) =>
new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', src, true);
xhr.onerror = reject;
xhr.onloadend = event => {
const blob = new Blob([xhr.response]);
const data = window.URL.createObjectURL(blob);
resolve({ data });
};
if (onProgress) {
xhr.onprogress = event => {
if (event.lengthComputable) {
onProgress({
total: event.total,
loaded: event.loaded,
ratio: event.loaded / event.total
});
}
};
}
xhr.send();
});
const useImage = src => {
const [progress, setProgress] = useState({ total: 0, loaded: 0, ratio: 0 });
const [response, setResponse] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await loadImage(src, setProgress);
setResponse(response);
} catch (error) {
setError(error);
}
};
fetchData();
}, []);
return { progress, response, error };
};
export default useImage;
import { useState, useCallback, useEffect } from "react";
const DELETE_KEY_CODE = 8;
export default function useInputFormated({ initialValue, ref, formatter }) {
const [value, setValue] = useState(initialValue);
const handleKeydown = useCallback(
event => {
const { key, keyCode } = event;
if (
(value === 0 && !/^[1-9]{1}$/.test(key)) ||
(value !== 0 && !/^[0-9]{1}$/.test(key) && keyCode !== DELETE_KEY_CODE)
) {
return;
}
let nextValue = value;
if (keyCode !== DELETE_KEY_CODE) {
const priceString = value === 0 ? key : `${value}${key}`;
const nextParsedValue = parseInt(priceString.replace(/\D/g, ""), 10);
nextValue = formatter(nextParsedValue / 100);
} else {
const valueString = `${value}`.slice(0, -1);
const nextPriceValue = parseInt(valueString.replace(/\D/g, ""), 10);
nextValue = isNaN(nextPriceValue)
? ""
: formatter(nextPriceValue / 100);
}
setValue(nextValue);
},
[formatter, value]
);
const handleChange = useCallback(() => null, []);
useEffect(() => {
const element = ref.current;
element.addEventListener("keydown", handleKeydown);
element.addEventListener("change", handleChange);
return () => {
element.removeEventListener("keydown", handleKeydown);
element.removeEventListener("change", handleChange);
};
}, [ref, handleKeydown, handleChange]);
return [value];
}
import { useEffect, useRef } from 'react'
/**
* Thanks to https://github.com/zaguiini that had sent the reference:
* Reference: https://css-tricks.com/restart-css-animation/#article-header-id-0
*/
export default function useReflow(...classes) {
const ref = useRef(null);
useEffect(() => {
const element = ref.current;
if (!element) return () => null
const handle = () => {
element.classList.remove(...classes);
// eslint-disable-next-line no-void
void element.offsetWidth;
element.classList.add(...classes);
}
element.addEventListener("DOMSubtreeModified", handle);
return () => {
element.removeEventListener("DOMSubtreeModified", handle)
}
}, [classes]);
return ref
}
import { useEffect, useState } from 'react';
const getState = () => ({
x: window.pageXOffset,
y: window.pageYOffset,
})
const useScrollPosition = () => {
const [state, setState] = useState({ x: 0, y : 0});
useEffect(() => {
let update
const onScroll = event => {
if (!update) {
update = window.requestAnimationFrame(() => {
setState(getState())
update = null;
})
}
};
setState(getState())
window.addEventListener('scroll', onScroll);
return () => {
window.removeEventListener('scroll', onScroll);
};
}, []);
return state;
};
export default useScrollPosition;
import { useEffect, useState } from 'react';
export default function useSearchQueryParams() {
const [params, setParams] = useState(null)
useEffect(() => {
setParams(new URLSearchParams(window.location.search))
// window.location.search could be replaced for any reference to location to perform right on server side
}, [setParams, window.location.search])
return params
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment