UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

86 lines 4.14 kB
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import * as React from 'react'; import { warnOnce } from '@awsui/component-toolkit/internal'; import { isDevelopment } from '../../is-development'; /** * This hook allows you to make a component that can be used both in controlled mode and uncontrolled mode. * Pass in your component's props, and then implement your component as if it was only controlled. * When calling onChange callbacks (or the equivalent for your property), you need to call both the callback returned by this function * as well as the callback provided in your component's props. * * A component determines its mode (either controlled or uncontrolled) on the first render and keeps it for its lifetime. The mode cannot * be switched later. * * * Example usage: * ```jsx * const [checked, setChecked] = useControllable( * props, * props.defaultEnabled ?? false, * { * componentName: 'MyCheckboxComponent', * controlledProp: 'enabled', * changeHandler: 'onCheckedStatusChange' * } * ) * * return * <input * type="checkbox" * checked={checked} * onChange={event => { * setChecked(event.target.checked); * fireNonCancelableEvent(props.onCheckedStatusChange, { checked: event.target.checked }) * }} /> * ``` * * @param controlledValue value for the controlled mode * @param handler update handler for controlled mode * @param defaultValue initial value for uncontrolled mode * @param description property metadata */ export function useControllable(controlledValue, handler, defaultValue, { componentName, changeHandler, controlledProp }) { // The decision whether a component is controlled or uncontrolled is made on its first render and cannot be changed afterwards. const isControlled = React.useState(controlledValue !== undefined)[0]; if (isDevelopment) { // Print a warning if the component switches between controlled and uncontrolled mode. // eslint-disable-next-line react-hooks/rules-of-hooks React.useEffect(() => { if (isControlled && handler === undefined) { warnOnce(componentName, `You provided a \`${controlledProp}\` prop without an \`${changeHandler}\` handler. This will render a non-interactive component.`); } }, [handler, isControlled, componentName, changeHandler, controlledProp]); // eslint-disable-next-line react-hooks/rules-of-hooks React.useEffect(() => { const isControlledNow = controlledValue !== undefined; if (isControlled !== isControlledNow) { const initialMode = isControlled ? 'controlled' : 'uncontrolled'; const modeNow = isControlledNow ? 'controlled' : 'uncontrolled'; warnOnce(componentName, `A component tried to change ${initialMode} '${controlledProp}' property to be ${modeNow}. ` + `This is not supported. Properties should not switch from ${initialMode} to ${modeNow} (or vice versa). ` + `Decide between using a controlled or uncontrolled mode for the lifetime of the component. ` + `More info: https://fb.me/react-controlled-components`); } }, [isControlled, controlledProp, componentName, controlledValue]); } // This is the value that is used if the component is uncontrolled. const [valueState, setValue] = React.useState(defaultValue); const [valueHasBeenSet, setValueHasBeenSet] = React.useState(false); // We track changes to the defaultValue const currentUncontrolledValue = valueHasBeenSet ? valueState : defaultValue; const setUncontrolledValue = React.useCallback((newValue) => { setValue(newValue); setValueHasBeenSet(true); }, [setValue, setValueHasBeenSet]); if (isControlled) { return [controlledValue, defaultCallback]; } else { return [currentUncontrolledValue, setUncontrolledValue]; } } function defaultCallback() { return void 0; } //# sourceMappingURL=index.js.map