UNPKG

@wordpress/components

Version:
8 lines (7 loc) 3.21 kB
{ "version": 3, "sources": ["../../../src/utils/hooks/use-controlled-state.js"], "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useEffect, useState, useCallback } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport { isValueDefined, getDefinedValue } from '../values';\n\n/**\n * @template T\n * @typedef Options\n * @property {T} [initial] Initial value\n * @property {T | \"\"} fallback Fallback value\n */\n\n/** @type {Readonly<{ initial: undefined, fallback: '' }>} */\nconst defaultOptions = {\n initial: undefined,\n /**\n * Defaults to empty string, as that is preferred for usage with\n * <input />, <textarea />, and <select /> form elements.\n */\n fallback: ''\n};\n\n/**\n * Custom hooks for \"controlled\" components to track and consolidate internal\n * state and incoming values. This is useful for components that render\n * `input`, `textarea`, or `select` HTML elements.\n *\n * https://reactjs.org/docs/forms.html#controlled-components\n *\n * At first, a component using useControlledState receives an initial prop\n * value, which is used as initial internal state.\n *\n * This internal state can be maintained and updated without\n * relying on new incoming prop values.\n *\n * Unlike the basic useState hook, useControlledState's state can\n * be updated if a new incoming prop value is changed.\n *\n * @template T\n *\n * @param {T | undefined} currentState The current value.\n * @param {Options<T>} [options=defaultOptions] Additional options for the hook.\n *\n * @return {[T | \"\", (nextState: T) => void]} The controlled value and the value setter.\n */\nfunction useControlledState(currentState, options = defaultOptions) {\n const {\n initial,\n fallback\n } = {\n ...defaultOptions,\n ...options\n };\n const [internalState, setInternalState] = useState(currentState);\n const hasCurrentState = isValueDefined(currentState);\n\n /*\n * Resets internal state if value every changes from uncontrolled <-> controlled.\n */\n useEffect(() => {\n if (hasCurrentState && internalState) {\n setInternalState(undefined);\n }\n }, [hasCurrentState, internalState]);\n const state = getDefinedValue([currentState, internalState, initial], fallback);\n\n /** @type {(nextState: T) => void} */\n const setState = useCallback(nextState => {\n if (!hasCurrentState) {\n setInternalState(nextState);\n }\n }, [hasCurrentState]);\n return [state, setState];\n}\nexport default useControlledState;"], "mappings": ";AAGA,SAAS,WAAW,UAAU,mBAAmB;AAKjD,SAAS,gBAAgB,uBAAuB;AAUhD,IAAM,iBAAiB;AAAA,EACrB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKT,UAAU;AACZ;AAyBA,SAAS,mBAAmB,cAAc,UAAU,gBAAgB;AAClE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,EACF,IAAI;AAAA,IACF,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACA,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,YAAY;AAC/D,QAAM,kBAAkB,eAAe,YAAY;AAKnD,YAAU,MAAM;AACd,QAAI,mBAAmB,eAAe;AACpC,uBAAiB,MAAS;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,iBAAiB,aAAa,CAAC;AACnC,QAAM,QAAQ,gBAAgB,CAAC,cAAc,eAAe,OAAO,GAAG,QAAQ;AAG9E,QAAM,WAAW,YAAY,eAAa;AACxC,QAAI,CAAC,iBAAiB;AACpB,uBAAiB,SAAS;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,eAAe,CAAC;AACpB,SAAO,CAAC,OAAO,QAAQ;AACzB;AACA,IAAO,+BAAQ;", "names": [] }