@atlaskit/form
Version:
A form allows people to input information.
60 lines (55 loc) • 2.5 kB
JavaScript
import { useContext, useEffect, useMemo, useState } from 'react';
import { FormContext } from './form';
// Constantized to avoid a new object reference built every call
const defaultSubscriptionConfig = {
values: true
};
/**
* Build a simple hash for a given subscription object for use in a `useMemo` dependencies array.
* This is because `{ values: true } !== { values: true }`, but `'values:true|' === 'values:true|'`
*
* @example { values: true, dirty: false } => 'values:true|dirty:false|'
*/
const getSubscriptionHash = subscriptionConfig => {
let hash = '';
for (const key in subscriptionConfig) {
if (subscriptionConfig.hasOwnProperty(key)) {
hash += `${key}:${subscriptionConfig[key]}|`;
}
}
return hash;
};
/**
* A hook to return a recent form state for use within the `<Form>` as it requires context access.
* This is useful for previewing form state, or for building custom fields that need to react to form state.
*
* This should not be used as a way to persist form state into another form state, use `onSubmit` for proper form handling.
*
* @note On the initial render, this should be `undefined` as our form has not provided any state.
*/
export const useFormState = (subscriptionConfig = defaultSubscriptionConfig) => {
const {
subscribe
} = useContext(FormContext);
const [state, setState] = useState();
/**
* A hash for us to shallow compare the subscriptionConfig object to react to shallow changes, but avoid referential changes.
* We avoid computing the hash if the subscription config has referential equality altogether.
*/
const subscriptionConfigHash = useMemo(() => getSubscriptionHash(subscriptionConfig), [subscriptionConfig]);
/**
* Return a memoized version of the subscription config to only react to shallow changes, not referential changes.
* Eg. calling `useFormState({ values: true })` twice will result in two different objects by reference, but not by shallow comparison.
* This will ensure we don't re-subscribe to the form state when the subscription config is the same.
*/
const config = useMemo(() => subscriptionConfig,
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally controlled with a hash to have an explicit shallow comparison
[subscriptionConfigHash]);
useEffect(() => {
const unsubscribe = subscribe(formState => {
setState(formState);
}, config);
return () => unsubscribe();
}, [subscribe, config]);
return state;
};