Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useImperativeHandle,
} from 'react';
import { useI18n } from '@18f/identity-react-i18n';
import { useIfStillMounted, useDidUpdateEffect } from '@18f/identity-react-hooks';
import AnalyticsContext from '../context/analytics';
import AcuantContext from '../context/acuant';
import FailedCaptureAttemptsContext from '../context/failed-capture-attempts';
Expand All @@ -18,8 +19,6 @@ import FullScreen from './full-screen';
import Button from './button';
import DeviceContext from '../context/device';
import UploadContext from '../context/upload';
import useIfStillMounted from '../hooks/use-if-still-mounted';
import useDidUpdateEffect from '../hooks/use-did-update-effect';
import useCounter from '../hooks/use-counter';
import useCookie from '../hooks/use-cookie';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useContext, useState } from 'react';
import { useDidUpdateEffect } from '@18f/identity-react-hooks';
import FailedCaptureAttemptsContext from '../context/failed-capture-attempts';
import AnalyticsContext from '../context/analytics';
import CallbackOnMount from './callback-on-mount';
import CaptureAdvice from './capture-advice';
import { FormStepsContext } from './form-steps';
import useDidUpdateEffect from '../hooks/use-did-update-effect';

/** @typedef {import('react').ReactNode} ReactNode */

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext, useState, useEffect } from 'react';
import useIfStillMounted from '../hooks/use-if-still-mounted';
import { useIfStillMounted } from '@18f/identity-react-hooks';
import FileBase64CacheContext from '../context/file-base64-cache';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useEffect, useRef, useState, createContext, useContext } from 'react';
import { useI18n } from '@18f/identity-react-i18n';
import { Alert } from '@18f/identity-components';
import { useDidUpdateEffect, useIfStillMounted } from '@18f/identity-react-hooks';
import Button from './button';
import FormErrorMessage, { RequiredValueMissingError } from './form-error-message';
import PromptOnNavigate from './prompt-on-navigate';
import useHistoryParam from '../hooks/use-history-param';
import useForceRender from '../hooks/use-force-render';
import useDidUpdateEffect from '../hooks/use-did-update-effect';
import useIfStillMounted from '../hooks/use-if-still-mounted';

/**
* @typedef FormStepError
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { forwardRef, useImperativeHandle, useRef, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { useI18n } from '@18f/identity-react-i18n';
import { useIfStillMounted } from '@18f/identity-react-hooks';
import useAsset from '../hooks/use-asset';
import useToggleBodyClassByPresence from '../hooks/use-toggle-body-class-by-presence';
import useImmutableCallback from '../hooks/use-immutable-callback';
import useFocusTrap from '../hooks/use-focus-trap';
import useIfStillMounted from '../hooks/use-if-still-mounted';

/** @typedef {import('focus-trap').FocusTrap} FocusTrap */
/** @typedef {import('react').ReactNode} ReactNode */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useContext, useState } from 'react';
import { hasMediaAccess } from '@18f/identity-device';
import { useI18n } from '@18f/identity-react-i18n';
import { useDidUpdateEffect } from '@18f/identity-react-hooks';
import { FormStepsContext, FormStepsContinueButton } from './form-steps';
import DeviceContext from '../context/device';
import DocumentSideAcuantCapture from './document-side-acuant-capture';
Expand All @@ -14,7 +15,6 @@ import PageHeading from './page-heading';
import StartOverOrCancel from './start-over-or-cancel';
import Warning from './warning';
import AnalyticsContext from '../context/analytics';
import useDidUpdateEffect from '../hooks/use-did-update-effect';

/**
* @typedef {'front'|'back'} DocumentSide
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
} from 'react';
import { Icon } from '@18f/identity-components';
import { useI18n } from '@18f/identity-react-i18n';
import { useIfStillMounted } from '@18f/identity-react-hooks';
import FileImage from './file-image';
import useIfStillMounted from '../hooks/use-if-still-mounted';
import useInstanceId from '../hooks/use-instance-id';
import useFocusFallbackRef from '../hooks/use-focus-fallback-ref';
import AppContext from '../context/app';
Expand Down
3 changes: 3 additions & 0 deletions app/javascript/packages/react-hooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `@18f/identity-react-hooks`

A collection of general-purpose [React hooks](https://reactjs.org/docs/hooks-intro.html).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth documenting the hooks in the README with examples? Or realistically these are internal so we should just look for internal usage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm conflicted, because on one hand, yes, it would be great to have documented examples. On the other hand, my experience is that those sorts of documentation fall out of sync with the implementation almost immediately. Ideally it would be something where we could generate the documentation out of the source code, but I'm not aware of any tooling that would produce what we'd want.

The main goal with including this was to try to be marginally better about documenting the purpose of each package for other developers on the team, so at least it's incrementally better in that regard 😅

2 changes: 2 additions & 0 deletions app/javascript/packages/react-hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as useDidUpdateEffect } from './use-did-update-effect';
export { default as useIfStillMounted } from './use-if-still-mounted';
8 changes: 8 additions & 0 deletions app/javascript/packages/react-hooks/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@18f/identity-react-hooks",
"private": true,
"version": "1.0.0",
"peerDependencies": {
"react": ">=16.8.0"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sinon from 'sinon';
import { renderHook } from '@testing-library/react-hooks';
import useDidUpdateEffect from '@18f/identity-document-capture/hooks/use-did-update-effect';
import useDidUpdateEffect from './use-did-update-effect';

describe('document-capture/hooks/use-did-update-effect', () => {
context('no dependencies', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import { useRef, useEffect } from 'react';
* A hook behaving the same as useEffect in invoking the given callback when dependencies change,
* but does not call the callback during initial mount or when unmounting. It can be considered as
* similar to ReactComponent#componentDidUpdate.
*
* @type {typeof useEffect}
*/
function useDidUpdateEffect(callback, deps) {
const useDidUpdateEffect: typeof useEffect = (callback, deps) => {
const isMounting = useRef(true);

useEffect(() => {
Expand All @@ -17,6 +15,6 @@ function useDidUpdateEffect(callback, deps) {
callback();
}
}, deps);
}
};

export default useDidUpdateEffect;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sinon from 'sinon';
import { renderHook } from '@testing-library/react-hooks';
import useIfStillMounted from '@18f/identity-document-capture/hooks/use-if-still-mounted';
import useIfStillMounted from './use-if-still-mounted';

describe('document-capture/hooks/use-if-still-mounted', () => {
it('returns function which executes callback if component is still mounted', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,12 @@ function useIfStillMounted() {
};
});

/**
* @template {(...args) => any} T
* @param {T} fn
*/
const ifStillMounted = (fn) =>
/** @type {T} */ (
(...args) => {
if (isMounted.current) {
fn(...args);
}
const ifStillMounted = <T extends (...args) => any>(fn: T) =>
((...args) => {
if (isMounted.current) {
fn(...args);
}
);
}) as T;

return ifStillMounted;
}
Expand Down