html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}[hidden],template{display:none}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,optgroup,select,textarea{font:inherit;margin:0}optgroup{font-weight:700}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}html{font:87.5%/1.2 -apple-system,'BlinkMacSystemFont','Segoe UI','Roboto','Oxygen-Sans','Ubuntu','Cantarell','Helvetica Neue',sans-serif;box-sizing:border-box;overflow-y:scroll;}*{box-sizing:inherit;}*:before{box-sizing:inherit;}*:after{box-sizing:inherit;}body{color:var(--app-foreground);font-family:-apple-system,'BlinkMacSystemFont','Segoe UI','Roboto','Oxygen-Sans','Ubuntu','Cantarell','Helvetica Neue',sans-serif;font-weight:normal;word-wrap:break-word;font-kerning:normal;-moz-font-feature-settings:"kern", "liga", "clig", "calt";-ms-font-feature-settings:"kern", "liga", "clig", "calt";-webkit-font-feature-settings:"kern", "liga", "clig", "calt";font-feature-settings:"kern", "liga", "clig", "calt";}img{max-width:100%;margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}h1{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;color:var(--app-purple);font-family:-apple-system,'BlinkMacSystemFont','Segoe UI','Roboto','Oxygen-Sans','Ubuntu','Cantarell','Helvetica Neue',sans-serif;font-weight:bold;text-rendering:optimizeLegibility;font-size:2rem;line-height:1.1;}h2{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;color:var(--app-purple);font-family:-apple-system,'BlinkMacSystemFont','Segoe UI','Roboto','Oxygen-Sans','Ubuntu','Cantarell','Helvetica Neue',sans-serif;font-weight:bold;text-rendering:optimizeLegibility;font-size:1.51572rem;line-height:1.1;}h3{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;color:var(--app-purple);font-family:-apple-system,'BlinkMacSystemFont','Segoe UI','Roboto','Oxygen-Sans','Ubuntu','Cantarell','Helvetica Neue',sans-serif;font-weight:bold;text-rendering:optimizeLegibility;font-size:1.31951rem;line-height:1.1;}h4{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;color:var(--app-purple);font-family:-apple-system,'BlinkMacSystemFont','Segoe UI','Roboto','Oxygen-Sans','Ubuntu','Cantarell','Helvetica Neue',sans-serif;font-weight:bold;text-rendering:optimizeLegibility;font-size:1rem;line-height:1.1;}h5{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;color:var(--app-purple);font-family:-apple-system,'BlinkMacSystemFont','Segoe UI','Roboto','Oxygen-Sans','Ubuntu','Cantarell','Helvetica Neue',sans-serif;font-weight:bold;text-rendering:optimizeLegibility;font-size:0.87055rem;line-height:1.1;}h6{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;color:var(--app-purple);font-family:-apple-system,'BlinkMacSystemFont','Segoe UI','Roboto','Oxygen-Sans','Ubuntu','Cantarell','Helvetica Neue',sans-serif;font-weight:bold;text-rendering:optimizeLegibility;font-size:0.81225rem;line-height:1.1;}hgroup{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}ul{margin-left:1.2rem;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;list-style-position:outside;list-style-image:none;}ol{margin-left:1.2rem;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;list-style-position:outside;list-style-image:none;}dl{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}dd{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}p{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}figure{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}pre{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;font-size:0.85rem;line-height:1.2rem;}table{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;font-size:1rem;line-height:1.8rem;border-collapse:collapse;width:100%;}fieldset{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}blockquote{margin-left:1.2rem;margin-right:1.2rem;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}form{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}noscript{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}iframe{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}hr{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:calc(1.2rem - 1px);background:hsla(0,0%,0%,0.2);border:none;height:1px;}address{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.2rem;}b{font-weight:bold;}strong{font-weight:bold;}dt{font-weight:bold;}th{font-weight:bold;}li{margin-bottom:calc(1.2rem / 2);}ol li{padding-left:0;}ul li{padding-left:0;}li > ol{margin-left:1.2rem;margin-bottom:calc(1.2rem / 2);margin-top:calc(1.2rem / 2);}li > ul{margin-left:1.2rem;margin-bottom:calc(1.2rem / 2);margin-top:calc(1.2rem / 2);}blockquote *:last-child{margin-bottom:0;}li *:last-child{margin-bottom:0;}p *:last-child{margin-bottom:0;}li > p{margin-bottom:calc(1.2rem / 2);}code{font-size:1em;line-height:1.4em;font-family:'Fira Code', monospace;}kbd{font-size:0.85rem;line-height:1.2rem;}samp{font-size:0.85rem;line-height:1.2rem;}abbr{border-bottom:1px dotted hsla(0,0%,0%,0.5);cursor:help;}acronym{border-bottom:1px dotted hsla(0,0%,0%,0.5);cursor:help;}abbr[title]{border-bottom:1px dotted hsla(0,0%,0%,0.5);cursor:help;text-decoration:none;}thead{text-align:left;}td,th{text-align:left;border-bottom:1px solid hsla(0,0%,0%,0.12);font-feature-settings:"tnum";-moz-font-feature-settings:"tnum";-ms-font-feature-settings:"tnum";-webkit-font-feature-settings:"tnum";padding-left:0.8rem;padding-right:0.8rem;padding-top:0.6rem;padding-bottom:calc(0.6rem - 1px);}th:first-child,td:first-child{padding-left:0;}th:last-child,td:last-child{padding-right:0;}a{color:inherit;}a:focus{outline:2px solid var(--app-cyan);outline-offset:0;}a:visited{color:var(--app-cyan);}code.inline{line-height:1em;background:none;}.bOFKFr{fill:currentColor;height:2em;margin:0.25em;-webkit-transform:none;-ms-transform:none;transform:none;}/*!sc*/ .etECvA{fill:currentColor;height:1em;margin:0.25em;-webkit-transform:none;-ms-transform:none;transform:none;}/*!sc*/ .crLEvQ{fill:currentColor;height:1em;margin:0.25em;-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);transform:rotate(-90deg);}/*!sc*/ .kpJAMR{fill:currentColor;height:1em;margin:0.25em;-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);}/*!sc*/ data-styled.g3[id="icon__Icon-sc-1w03ldy-0"]{content:"bOFKFr,etECvA,crLEvQ,kpJAMR,"}/*!sc*/ .empDfs{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:1em;border:1px solid var(--app-foreground);border-radius:var(--app-border-radius);-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}/*!sc*/ data-styled.g4[id="example__Render-sc-1u0tgm1-0"]{content:"empDfs,"}/*!sc*/ .iXseCQ{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end;margin-top:0.25em;}/*!sc*/ data-styled.g5[id="example__FlexRight-sc-1u0tgm1-1"]{content:"iXseCQ,"}/*!sc*/ .eBWeFI{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ data-styled.g6[id="example__CodeSandboxLink-sc-1u0tgm1-2"]{content:"eBWeFI,"}/*!sc*/ .iyGSwu{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin:0;padding:0;color:var(--app-foreground);background:none;border:none;cursor:pointer;}/*!sc*/ .iyGSwu:focus,.iyGSwu:active{color:var(--app-cyan);outline:2px solid var(--app-cyan);}/*!sc*/ data-styled.g7[id="example__ResetButton-sc-1u0tgm1-3"]{content:"iyGSwu,"}/*!sc*/ .pTvQc{max-width:100%;overflow:auto;--grvsc-border-radius:var(--app-border-radius);--grvsc-padding-h:1em;--grvsc-padding-v:1em;}/*!sc*/ .dense .default-mdx-provider__pre-sc-15gdqwx-0{margin:0;}/*!sc*/ data-styled.g8[id="default-mdx-provider__pre-sc-15gdqwx-0"]{content:"pTvQc,"}/*!sc*/ .hoMCCU.slug{font-size:0.75em;margin-left:0.5em;display:none;-webkit-text-decoration:none;text-decoration:none;}/*!sc*/ *:hover > .default-mdx-provider__a-sc-15gdqwx-2.slug{display:unset;}/*!sc*/ data-styled.g10[id="default-mdx-provider__a-sc-15gdqwx-2"]{content:"hoMCCU,"}/*!sc*/ .koMXVa{margin:3em 0 1em;text-align:center;}/*!sc*/ data-styled.g12[id="footer__Container-sc-1pf7cvd-0"]{content:"koMXVa,"}/*!sc*/ .dvvVYp{color:var(--app-comment);}/*!sc*/ data-styled.g13[id="footer__Text-sc-1pf7cvd-1"]{content:"dvvVYp,"}/*!sc*/ .kCIvmz{color:var(--app-comment);}/*!sc*/ .kCIvmz:visited{color:var(--app-comment);}/*!sc*/ data-styled.g14[id="footer__Link-sc-1pf7cvd-2"]{content:"kCIvmz,"}/*!sc*/ .eeUdUW{display:grid;grid-template-areas:'title expand' 'links links';grid-template-columns:1fr auto;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}/*!sc*/ @media (min-width:1200px){.eeUdUW{grid-template-areas:'title' 'links';grid-template-columns:unset;}}/*!sc*/ data-styled.g15[id="navigation__Container-sc-18boyty-0"]{content:"eeUdUW,"}/*!sc*/ .khCaEg{grid-area:title;margin:0;}/*!sc*/ @media (min-width:1200px){.khCaEg{margin-bottom:0.5em;}}/*!sc*/ data-styled.g16[id="navigation__Title-sc-18boyty-1"]{content:"khCaEg,"}/*!sc*/ .jyqapB{-webkit-text-decoration:none;text-decoration:none;color:var(--app-foreground);}/*!sc*/ .jyqapB:visited{color:var(--app-foreground);}/*!sc*/ data-styled.g17[id="navigation__TitleLink-sc-18boyty-2"]{content:"jyqapB,"}/*!sc*/ .iKXeho{grid-area:expand;color:var(--app-foreground);width:50px;height:50px;background:none;border:none;margin:0;padding:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}/*!sc*/ .iKXeho:focus,.iKXeho:active{color:var(--app-pink);outline:2px solid var(--app-pink);}/*!sc*/ @media (min-width:1200px){.iKXeho{display:none;}}/*!sc*/ data-styled.g18[id="navigation__Expand-sc-18boyty-3"]{content:"iKXeho,"}/*!sc*/ .ktoYoz{grid-area:links;display:none;}/*!sc*/ @media (min-width:1200px){.ktoYoz{display:unset;}}/*!sc*/ data-styled.g19[id="navigation__Links-sc-18boyty-4"]{content:"ktoYoz,"}/*!sc*/ .bwEVJA{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:1.2em;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}/*!sc*/ data-styled.g20[id="prev-next__StyledLinkToSlug-sel85m-0"]{content:"bwEVJA,"}/*!sc*/ .fhnjmd{display:grid;grid-template-areas:'left-void nav right-void' 'left-void content right-void';grid-template-columns:1fr minmax(0,85ch) 1fr;padding:0 0.5rem;}/*!sc*/ @media (min-width:1200px){.fhnjmd{grid-template-areas:'nav content void';grid-template-columns:1fr 85ch 1fr;padding:1rem;max-width:150ch;margin:0 auto;}}/*!sc*/ data-styled.g21[id="default__Grid-cc4rlb-0"]{content:"fhnjmd,"}/*!sc*/ .giaaSE{grid-area:nav;}/*!sc*/ data-styled.g22[id="default__NavigationSlot-cc4rlb-1"]{content:"giaaSE,"}/*!sc*/ .hVUruk{grid-area:content;}/*!sc*/ data-styled.g23[id="default__Content-cc4rlb-2"]{content:"hVUruk,"}/*!sc*/ .iBPGgH{display:grid;grid-template-areas:'next' 'prev';}/*!sc*/ @media (min-width:576px){.iBPGgH{grid-template-areas:'prev void next';grid-template-columns:auto 1fr auto;}}/*!sc*/ data-styled.g24[id="default__Paging-cc4rlb-3"]{content:"iBPGgH,"}/*!sc*/ .bDSAqh{grid-area:prev;}/*!sc*/ data-styled.g25[id="default__StyledPrev-cc4rlb-4"]{content:"bDSAqh,"}/*!sc*/ .dmuBWa{grid-area:next;justify-self:flex-end;}/*!sc*/ data-styled.g26[id="default__StyledNext-cc4rlb-5"]{content:"dmuBWa,"}/*!sc*/

Hooks

  • allows state and side-effects in function components
  • simple to use, but follow the rules
  • create custom hooks, great for encapsulating common functionality

Rules

  • only call hooks at the top level
    • don’t call hooks inside loops, conditions, or nested functions
    • rationale:
      • same hook can be used multiple times
      • they must be called in the same order every time, otherwise React does not know which hook is which
  • only call hooks from React functions
    • don’t call hooks from regular JavaScript functions
    • rationale:
      • hook state is bound to the component instance that is being rendered
  • (convention) custom hooks start with use

useState

  • const [currentState, setNextStateFn] = useState(initialValue?)
  • use for internal state that affects DOM
  • takes initial value; returns a tuple of the current value and a function to update it
  • docs
import React, { FC, useState } from 'react'

export const Counter: FC = () => {
  const [value, setValue] = useState(0)

  return (
    <>
      <button type="button" onClick={() => setValue(value - 1)}>
        -
      </button>
      <span> {value} </span>
      <button type="button" onClick={() => setValue(value + 1)}>
        +
      </button>
    </>
  )
}

export default <Counter />
0

useRef

  • const ref = useRef(initialValue?)
  • use for referencing elements, e.g. to use their methods
  • docs
import React, { FC, useRef } from 'react'

export const PlayPause: FC = () => {
  const videoRef = useRef<HTMLVideoElement>(null)
  const playPause = async () => {
    const video = videoRef.current as HTMLVideoElement
    if (video.paused) {
      await video.play()
    } else {
      video.pause()
    }
  }

  return (
    <>
      <video
        ref={videoRef}
        autoPlay
        loop
        src="https://media.tenor.com/videos/632c96bbc411d8baa3f7f43692474808/webm"
        aria-label="video"
      />
      <br />
      <button type="button" onClick={playPause}>

      </button>
    </>
  )
}

export default <PlayPause />

  • use for internal state that does not affect DOM, e.g. timer handles
import React, { FC, useRef, useState } from 'react'

export const SelfDestruct: FC = () => {
  const [destroyed, setDestroyed] = useState(false)
  const timeoutRef = useRef<number>()

  const cancel = () => {
    window.clearTimeout(timeoutRef.current)
    timeoutRef.current = undefined
  }
  const start = () => {
    cancel()
    timeoutRef.current = window.setTimeout(() => {
      setDestroyed(true)
    }, 2000)
  }

  return destroyed ? null : (
    <>
      <button type="button" onClick={start}>
        start self-destruct sequence
      </button>
      <button type="button" onClick={cancel}>
        cancel self-destruct sequence
      </button>
      <p>this message will destroy itself in 2 seconds</p>
    </>
  )
}

export default <SelfDestruct />

this message will destroy itself in 2 seconds

useEffect

  • useEffect(effectFn, deps?)
  • use for side-effects
  • effectFn: function that can have side-effects; optionally return cleanup function
  • deps: a list of values that the side-effect depends on (optional)
  • when dependencies change, the effect is cleaned up and ran again
  • if no dependencies are given, then the effect runs on every render
  • docs
import React, { FC, useEffect, useState } from 'react'

export const LocaleClock: FC<{ locale?: string }> = ({ locale = 'en-US' }) => {
  const [timeString, setTimeString] = useState<string>()
  useEffect(() => {
    const update = () => {
      setTimeString(new Date().toLocaleTimeString(locale))
    }
    update()

    const interval = window.setInterval(update, 500)

    return () => {
      window.clearInterval(interval)
    }
  }, [locale])

  return <p>{timeString}</p>
}

export const ToggleLocale: FC = () => {
  const [locale, setLocale] = useState('en-GB')
  const toggleLocale = () => {
    setLocale(locale === 'en-GB' ? 'th-TH-u-nu-thai' : 'en-GB')
  }

  return (
    <>
      <button type="button" onClick={toggleLocale}>
        toggle locale
      </button>
      <LocaleClock locale={locale} />
    </>
  )
}

export default <ToggleLocale />