8,037 Matching Annotations
  1. Mar 2023
    1. ```js

      export const loader = async () => {

      // fire them all at once<br /> const critical1Promise = fetch('/test?text=critical1&delay=250').then(res => res.json()); const critical2Promise = fetch('/test?text=critical2&delay=500').then(res => res.json()); const lazyResolvedPromise = fetch('/test?text=lazyResolved&delay=100').then(res => res.json()); const lazy1Promise = fetch('/test?text=lazy1&delay=500').then(res => res.json()); const lazy2Promise = fetch('/test?text=lazy2&delay=1500').then(res => res.json()); const lazy3Promise = fetch('/test?text=lazy3&delay=2500').then(res => res.json()); const lazyErrorPromise = fetch('/test?text=lazy3&delay=3000').then(res => { throw Error('Oh noo!') });

      // await for the response return defer({ critical1: await critical1Promise, critical2: await critical2Promise, lazyResolved: lazyResolvedPromise, lazy1: lazy1Promise, lazy2: lazy2Promise, lazy3: lazy3Promise, lazyError: lazyErrorPromise }) } ```

  2. hydrogen.shopify.dev hydrogen.shopify.dev
    1. ```js import {defer} from "@shopify/remix-oxygen";

      export async function loader({ params: {handle}, context: {storefront} }) { const {product} = storefront.query({ query: #graphql query Product( $country: CountryCode, $language: LanguageCode, $handle: String! ) @inContext(country: $country, language: $language) product(handle: $handle) { id title }, variables: {handle}, cache: storefront.CacheLong() }); const {productRecommendations} = storefront.query({ query: #graphql query ProductRecommendations( $country: CountryCode, $language: LanguageCode, $handle: String! ) @inContext(country: $country, language: $language) productRecommendations(handle: $handle) { id title } }, variables: {handle} }); if (!product) { throw new Response('Not Found', { status: 404, }); } return defer({ product: await product, productRecommendations, }); } ```

    1. We present deferred data by using React Suspense to conditionally show the content when it's ready. Suspense provides a fallback element to show when the data is not yet ready. Normally a loading spinner would go here, but we can use that to show our streamed progress instead.

      js export default function Index() { const data = useLoaderData() const params = useParams() const stream = useEventSource( `/items/${params.hash}/progress`, { event: "progress", }, ) return ( <div> <Suspense fallback={<span> {stream}% </span>}> <Await resolve={data.promise} errorElement={<p>Error loading img!</p>} > {(promise) => <img alt="" src={promise.img} />} </Await> </Suspense> </div> ) }

    2. On the client, while we're waiting for our deferred promise to resolve, we can consume that stream to know how far along our process is.

      js const stream = useEventSource( `/items/${params.hash}/progress`, { event: "progress", }, )

    3. In Remix, we can use a resource route to make this endpoint, and our loader will return a stream that constant checks our JSON file for its progress.

      js export async function loader({ request, params, }: LoaderArgs) { const hash = params.hash return eventStream(request.signal, function setup(send) { const interval = setInterval(() => { const file = fs.readFileSync( path.join("public", "items", `${hash}.json`), ) if (file.toString()) { const data = JSON.parse(file.toString()) const progress = data.progress send({ event: "progress", data: String(progress) }) if (progress === 100) { clearInterval(interval) } } }, 200) return function clear(timer: number) { clearInterval(interval) clearInterval(timer) } }) }

    4. server sent events work by having an endpoint that does not immediately close its connection, and which sends a content type of text/event-stream.
    5. The loader defers a promise that will resolve only when the json's progress has hit 100.

      js export async function loader({ params }: LoaderArgs) { if (!params.hash) return redirect("/") const pathname = path.join( "public", "items", `${params.hash}.json`, ) const file = fs.readFileSync(pathname) if (!file) return redirect("/") const item = JSON.parse(file.toString()) if (!item) return redirect("/") if (item.progress === 100) { return defer({ promise: item, }) } return defer({ promise: new Promise((resolve) => { const interval = setInterval(() => { const file = fs.readFileSync(pathname) if (!file) return const item = JSON.parse(file.toString()) if (!item) return if (item.progress === 100) { clearInterval(interval) resolve(item) } return }) }), }) }

    6. Defer is a feature of Remix that allows you to return an unresolved Promise from a loader. The page will server-side render without waiting for the promise to resolve, and then when it finally does, the client will re-render with the new data.
    1. ```js function makeDocument() { let frame = document.getElementById("theFrame");

      let doc = document.implementation.createHTMLDocument("New Document"); let p = doc.createElement("p"); p.textContent = "This is a new paragraph.";

      try { doc.body.appendChild(p); } catch (e) { console.log(e); }

      // Copy the new HTML document into the frame

      let destDocument = frame.contentDocument; let srcNode = doc.documentElement; let newNode = destDocument.importNode(srcNode, true);

      destDocument.replaceChild(newNode, destDocument.documentElement); } ```

    1. Suspense itself will show fallbacks when not ready and reveal the contents when promises resolve, problem is that if there are multiple Suspense components, it could lead to flickering because of the order is not assured, that’s why we need sorta coordinating. SuspenseList is exactly for this.

      ```html

      <div>Hi</div>

      <React.SuspenseList revealOrder="forwards"> <React.Suspense fallback={\<p>loading...\

      }> <Child resource={resource1} /> </React.Suspense> <React.Suspense fallback={\<p>loading...\

      }> <Child resource={resource2} /> </React.Suspense> <React.Suspense fallback={\<p>loading...\

      }> <Child resource={resource3} /> </React.Suspense> </React.SuspenseList> ```

      We can see that the revealing order is kept, from top to bottom, even though the 2nd promise if fulfilled sooner.

    1. Fortunately with webpackChunkName in an inline comment, we can instruct webpack to name the file something much friendlier.

      js const HeavyComponent = lazy(() => import(/* webpackChunkName: "HeavyComponent" */ './HeavyComponent');

    1. Similar to prefetching, you can specify the chunk name with an inline comment using the webpackChunkName key

      js const LazyLoad = lazy(() => import(/* webpackChunkName: 'lazyload' */ './LazyLoad') );

    2. For example, if the user is on the login page, you can load the post-login home page. An inline comment with the webpackPrefetch key within the dynamic import function will instruct Webpack to do just this.

      ```js const PrefetchLoad = lazy(() => import( / webpackPrefetch: true / './PrefetchLoad' ) );

      const App = () => { return ( <Suspense fallback="Loading"> {condition && <PrefetchLoad />} </Suspense> ); }; ```

    3. Now when App is rendered and a request is initiated to get the LazyLoad code, the fallback Loading is rendered. When this request completes, React will then render LazyLoad.

      js const App = () => { return ( <Suspense fallback="Loading"> <LazyLoad /> </Suspense> ); };

    4. Now, App and LazyLoad are in separate code chunks. A request is sent to fetch the LazyLoad code chunk only when App is rendered. When the request completes, React will then renderLazyLoad. You can verify this by looking at the Javascript requests in the network inspector.

      ```js import React, { lazy } from 'react';

      const LazyLoad = lazy(() => import('./LazyLoad'));

      const App = () => { return <LazyLoad />; }; ```

    1. You can have multiple nested React.lazy components inside React.Suspense.

      ```js

      const CatAvatar = React.lazy(() => import('./path/to/cat/avatar')); const ThorAvatar = React.lazy(() => import('./path/to/cat/thor-avatar'));

      const AppContainer = () => ( <React.Suspense fallback="loading..."> <CatAvatar /> <ThorAvatar /> </React.Suspense> ); ```

    1. Chunked encoding is useful when larger amounts of data are sent to the client and the total size of the response may not be known until the request has been fully processed. For example, when generating a large HTML table resulting from a database query or when transmitting large images.A chunked response looks like this:

      ```http HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked

      7\r\n Mozilla\r\n 11\r\n Developer Network\r\n 0\r\n \r\n ```

    2. chunked Data is sent in a series of chunks. The Content-Length header is omitted in this case and at the beginning of each chunk you need to add the length of the current chunk in hexadecimal format, followed by '\r\n' and then the chunk itself, followed by another '\r\n'. The terminating chunk is a regular chunk, with the exception that its length is zero. It is followed by the trailer, which consists of a (possibly empty) sequence of header fields.
    3. ```abnf Transfer-Encoding: chunked Transfer-Encoding: compress Transfer-Encoding: deflate Transfer-Encoding: gzip

      // Several values can be listed, separated by a comma Transfer-Encoding: gzip, chunked ```

    1. Now we have autocomplete and typechecking.

      ```ts type ColorDSValue = "black100" | "black80" | "blue100" | "red150" // | etc type ColorOtherString = string & {}

      type Color = ColorDSValue | ColorOtherString ```

    2. This is so so useful for types or props where you want the general type for support (string), but you also want the specific type for autocomplete ("black100"). It made my whole week when I figured that out and made that color type.
    3. This weird-looking intersection of string & {} makes it so that the specific strings "hello" and "world" are distinguished from string as a whole type.

      ts const wow: ("hello" | "world") | (string & {}) // `wow` is of type `"hello"` or `"world"` or `string`.


    1. CSS-generated content is not included in the DOM. Because of this, it will not be represented in the accessibility tree and certain assistive technology/browser combinations will not announce it. If the content conveys information that is critical to understanding the page's purpose, it is better to include it in the main document.
    2. ```abnf content = normal | none | [ <content-replacement> | <content-list> ] [ / [ <string> | <counter> ]+ ]? | element( )

      <content-replacement> = <image>

      <content-list> = [ <string> | contents | <image> | <counter> | <quote> | <target> | <leader()> ]+

      <counter> = <counter()> | <counters()>

      <image> = <url> | <gradient>

      <quote> = open-quote | close-quote | no-open-quote | no-close-quote

      <target> = <target-counter()> | <target-counters()> | <target-text()>

      <leader()> = leader( <leader-type> )

      <counter()> = counter( <counter-name> , <counter-style>? )

      <counters()> = counters( <counter-name> , <string> , <counter-style>? )

      <url> = url( <string> <url-modifier> ) | src( <string> <url-modifier> )

      <target-counter()> = target-counter( [ <string> | <url> ] , <custom-ident> , <counter-style>? )

      <target-counters()> = target-counters( [ <string> | <url> ] , <custom-ident> , <string> , <counter-style>? )

      <target-text()> = target-text( [ <string> | <url> ] , [ content | before | after | first-letter ]? )

      <leader-type> = dotted | solid | space | <string>

      <counter-style> = <counter-style-name> | <symbols()>

      <symbols()> = symbols( <symbols-type>? [ <string> | <image> ]+ )

      <symbols-type> = cyclic | numeric | alphabetic | symbolic | fixed <br /> ```

    1. ```html

      <div data-size="large" class="data-[size=large]:p-8"> </div> <div data-size="medium" class="data-[size=large]:p-8"> </div> <style> .data-\[size\=large\]\:p-8[data-size="large"] { padding: 2rem; } </style>

      ```

    1. Remix uses the ?index parameter to indicate when a URL refers to the index route instead of the layout route
      • Matches by Object property ```js const { matches } = require('z')

      const person = { name: 'Maria' } matches(person)( (x = { name: 'John' }) => console.log('John you are not welcome!'), (x) => console.log(Hey ${x.name}, you are welcome!) ) - Matches by type or instancesjs const { matches } = require('z')

      const result = matches(1)( (x = 2) => 'number 2 is the best!!!', (x = Number) => number ${x} is not that good, (x = Date) => 'blaa.. dates are awful!' )

      console.log(result) // output: number 1 is not that good - Matches Array contentjs const { matches } = require('z')

      matches([1, 2, 3, 4, 5])( (a, b, c, tail) => 'a = 1, b = 2, c = 3, tail = [4, 5]'<br /> )

      matches([1, 2])( (a, tail) => 'a = 1, tail = [2]'<br /> )

      matches([1])( (a, b, tail) => 'Will not match here', (a = 2, tail = []) => 'Will not match here', (a = 1, tail) => 'Will match here, tail = []' ) ```

      • Powerful recursive code which will remove sequential repeated items from Array ```js const { matches } = require('z')

      const compress = (numbers) => { return matches(numbers)( (x, y, xs) => x === y ? compress([x].concat(xs)) : [x].concat(compress([y].concat(xs))), (x, xs) => x // stopping condition ) }

      compress([1, 1, 2, 3, 4, 4, 4]) //output: [1, 2, 3, 4] ```

    1. ```abnf object-position = <position>

      <position> = [ left | center | right ] || [ top | center | bottom ] | [ left | center | right | <length-percentage> ] [ top | center | bottom | <length-percentage> ]? | [ [ left | right ] <length-percentage> ] && [ [ top | bottom ] <length-percentage> ]

      <length-percentage> = <length> | <percentage> ```

    1. Microdata DOM API

      Warning: Microdata were implemented in some browsers for a long time. Nowadays, they have been abandoned and removed from all browsers and are therefore deprecated. You can't use them anymore and this document is kept as information only.

    1. The microdata becomes even more useful when scripts can use it to expose information to the user, for example offering it in a form that can be used by other applications.The document.getItems(typeNames) method provides access to the top-level microdata items. It returns a NodeList containing the items with the specified types, or all types if no argument is specified.Each item is represented in the DOM by the element on which the relevant itemscope attribute is found. These elements have their element.itemScope IDL attribute set to true.The type of items can be obtained using the element.itemType IDL attribute on the element with the itemscope attribute.

      ```js // This sample shows how the getItems() method can be used to obtain a list // of all the top-level microdata items of one type given in the document:

      var cats = document.getItems("http://example.com/feline"); ```

      ```js // This sample gets the first item of type "http://example.net/user" // and then pops up an alert using the "name" property from that item.

      var user = document.getItems('http://example.net/user')[0]; alert('Hello ' + user.properties['name'][0].content + '!'); ```

      ```js // The HTMLPropertiesCollection object, when indexed by name in this way, // actually returns a PropertyNodeList object with all the matching properties. // The PropertyNodeList object can be used to obtain all the values at once // using its values attribute, which returns an array of all the values.

      var cat = document.getItems('http://example.org/animals#cat')[0]; var colors = cat.properties['http://example.com/color'].values; var result; if (colors.length == 0) { result = 'Color unknown.'; } else if (colors.length == 1) { result = 'Color: ' + colors[0]; } else { result = 'Colors:'; for (var i = 0; i < colors.length; i += 1) result += ' ' + colors[i]; } ```