React

useMediaQuery Custom Hook

Conditionally render components based on the browser size rather than rendering and hiding the components with CSS. Taken from @cassidoo's post Building a custom React media query hook for more responsive apps

import { useState, useEffect } from 'react';

export function useMediaQuery(query) {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const media = window.matchMedia(query);
    if (media.matches !== matches) {
      setMatches(media.matches);
    }
    const listener = () => {
      setMatches(media.matches);
    };
    media.addListener(listener);
    return () => media.removeListener(listener);
  }, [matches, query]);

  return matches;
}

// how to use it
function Page() {
  let isPageWide = useMediaQuery('(min-width: 800px)');

  return (
    <>
      {isPageWide && <UnnecessarySidebar />}
      <ImportantContent />
    </>
  );
}
Updated: 6 February 2022

Pass props to props children

A very basic example of how we can pass props down to props.children. If you're wanting to abstract away some logic, make your code more readable or make it easy to switch out components, this is a great solution.

// inside our component we add the props we want as arguments for props.children
const Blocks = ({ allBlocks, children }) =>
  allBlocks.map((block, index) => <div>{children(block, index)}</div>);

// then we can access those arguments via a function
<Blocks allBlocks={state.allBlocks}>
  {(block, index) => {
    return <DynamicBlock block={block} index={index} />;
  }}
</Blocks>;
Updated: 15 February 2022

Autosize Textarea

Create a Textarea that auto resizes as you add more content. It makes for a much nicer experience.

export const TextArea = ({
  label,
  name,
  defaultValue,
  rows = 5,
}: TextAreaProps) => {
  const textarea = useRef<HTMLTextAreaElement>(null)
  const [characterCount, setCharacterCount] = useState(
    defaultValue?.length || 0,
  )

  // if we change the line height in css make sure to change this
  let lineHeight = 1.75
  let initialLimit = rows * (lineHeight * 16)
  const handleChange = (e: React.ChangeEvent<any>) => {
    let scrollLeft =
      window.pageXOffset ||
      (document.documentElement || document.body.parentNode || document.body)
        .scrollLeft
    let scrollTop =
      window.pageYOffset ||
      (document.documentElement || document.body.parentNode || document.body)
        .scrollTop
    e.target.style.height = 'auto'
    e.target.style.height = `${Math.max(e.target.scrollHeight, initialLimit)}px`
    window.scrollTo(scrollLeft, scrollTop)
  }

  useEffect(() => {
    if (textarea.current) {
      textarea.current.style.height = 'inherit'
      textarea.current.style.height = `${Math.max(
        textarea.current.scrollHeight,
        initialLimit,
      )}px`
    }
  }, [])

  return (
    <div className="flex flex-col gap-2 w-full relative">
      <label htmlFor={name}>{label}</label>
      <textarea
        ref={textarea}
        className="border-slate-300 border w-full p-3 text-black leading-7 rounded text-base resize-none overflow-hidden disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed"
        name={name}
        id={name}
        rows={rows}
        defaultValue={defaultValue}
        spellCheck={true}
        onChange={(e) => {
          handleChange(e)
        }}
      ></textarea>
    </div>
  )
}
Updated: 8 February 2022