UNPKG

jobiqo-cl

Version:

[![CircleCI](https://circleci.com/gh/jobiqo/jobiqo-cl.svg?style=svg&circle-token=5a24efa5b8bbc4879276123e77d0d3f35ca7144c)](https://circleci.com/gh/jobiqo/jobiqo-cl)

107 lines (96 loc) 4.02 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = require('react'); var React__default = _interopDefault(React); /* * Let's see if we can make sense of why this hook exists and its * implementation. * * Some background: * 1. Accessibiliy APIs rely heavily on element IDs * 2. Requiring developers to put IDs on every element in Reach UI is both * cumbersome and error-prone * 3. With a component model, we can generate IDs for them! * * Solution 1: Generate random IDs. * * This works great as long as you don't server render your app. When React (in * the client) tries to reuse the markup from the server, the IDs won't match * and React will then recreate the entire DOM tree. * * Solution 2: Increment an integer * * This sounds great. Since we're rendering the exact same tree on the server * and client, we can increment a counter and get a deterministic result between * client and server. Also, JS integers can go up to nine-quadrillion. I'm * pretty sure the tab will be closed before an app never needs * 10 quadrillion IDs! * * Problem solved, right? * * Ah, but there's a catch! React's concurrent rendering makes this approach * non-deterministic. While the client and server will end up with the same * elements in the end, depending on suspense boundaries (and possibly some user * input during the initial render) the incrementing integers won't always match * up. * * Solution 3: Don't use IDs at all on the server; patch after first render. * * What we've done here is solution 2 with some tricks. With this approach, the * ID returned is an empty string on the first render. This way the server and * client have the same markup no matter how wild the concurrent rendering may * have gotten. * * After the render, we patch up the components with an incremented ID. This * causes a double render on any components with `useId`. Shouldn't be a problem * since the components using this hook should be small, and we're only updating * the ID attribute on the DOM, nothing big is happening. * * It doesn't have to be an incremented number, though--we could do generate * random strings instead, but incrementing a number is probably the cheapest * thing we can do. * * Additionally, we only do this patchup on the very first client render ever. * Any calls to `useId` that happen dynamically in the client will be * populated immediately with a value. So, we only get the double render after * server hydration and never again, SO BACK OFF ALRIGHT? */ var serverHandoffComplete = false; var id = 0; var genId = function genId() { return ++id; }; var useId = function useId(fallback) { /* * If this instance isn't part of the initial render, we don't have to do the * double render/patch-up dance. We can just generate the ID and return it. */ var initialId = fallback || (serverHandoffComplete ? genId() : null); var _useState = React.useState(initialId), id = _useState[0], setId = _useState[1]; React.useLayoutEffect(function () { if (id === null) { /* * Patch the ID after render. We do this in `useLayoutEffect` to avoid any * rendering flicker, though it'll make the first render slower (unlikely * to matter, but you're welcome to measure your app and let us know if * it's a problem). */ setId(genId()); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); React.useEffect(function () { if (serverHandoffComplete === false) { /* * Flag all future uses of `useId` to skip the update dance. This is in * `useEffect` because it goes after `useLayoutEffect`, ensuring we don't * accidentally bail out of the patch-up dance prematurely. */ serverHandoffComplete = true; } }, []); return id; }; exports.useId = useId;