Outlook_Addin_LLM/node_modules/@fluentui/react-utilities/lib/hooks/useControllableState.js

92 lines
4.0 KiB
JavaScript

import * as React from 'react';
function isFactoryDispatch(newState) {
return typeof newState === 'function';
}
/**
* @internal
*
* A [`useState`](https://reactjs.org/docs/hooks-reference.html#usestate)-like hook
* to manage a value that could be either `controlled` or `uncontrolled`,
* such as a checked state or text input string.
*
* @see https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components for more details on `controlled`/`uncontrolled`
*
* @returns an array of the current value and an updater (dispatcher) function.
* The updater function is referentially stable (won't change during the component's lifecycle).
* It can take either a new value, or a function which is passed the previous value and returns the new value.
*
* ❗️❗️ Calls to the dispatcher will only modify the state if the state is `uncontrolled`.
* Meaning that if a state is `controlled`, calls to the dispatcher do not modify the state.
*
*/ export const useControllableState = (options)=>{
'use no memo';
if (process.env.NODE_ENV !== 'production') {
if (options.state !== undefined && options.defaultState !== undefined) {
// eslint-disable-next-line no-console
console.error(`@fluentui/react-utilities [useControllableState]:
A component must be either controlled or uncontrolled (specify either the state or the defaultState, but not both).
Decide between using a controlled or uncontrolled component and remove one of this props.
More info: https://reactjs.org/link/controlled-components
${new Error().stack}`);
}
}
const [internalState, setInternalState] = React.useState(()=>{
if (options.defaultState === undefined) {
return options.initialState;
}
return isInitializer(options.defaultState) ? options.defaultState() : options.defaultState;
});
// Heads up!
// This part is specific for controlled mode and mocks behavior of React dispatcher function.
const stateValueRef = React.useRef(options.state);
React.useEffect(()=>{
stateValueRef.current = options.state;
}, [
options.state
]);
const setControlledState = React.useCallback((newState)=>{
if (isFactoryDispatch(newState)) {
newState(stateValueRef.current);
}
}, []);
return useIsControlled(options.state) ? [
options.state,
setControlledState
] : [
internalState,
setInternalState
];
};
function isInitializer(value) {
return typeof value === 'function';
}
/**
* Helper hook to handle previous comparison of controlled/uncontrolled
* Prints an error when isControlled value switches between subsequent renders
* @returns - whether the value is controlled
*/ const useIsControlled = (controlledValue)=>{
'use no memo';
const [isControlled] = React.useState(()=>controlledValue !== undefined);
if (process.env.NODE_ENV !== 'production') {
// We don't want these warnings in production even though it is against native behaviour
// eslint-disable-next-line react-hooks/rules-of-hooks
React.useEffect(()=>{
if (isControlled !== (controlledValue !== undefined)) {
const error = new Error();
const controlWarning = isControlled ? 'a controlled value to be uncontrolled' : 'an uncontrolled value to be controlled';
const undefinedWarning = isControlled ? 'defined to an undefined' : 'undefined to a defined';
// eslint-disable-next-line no-console
console.error(`@fluentui/react-utilities [useControllableState]:
A component is changing ${controlWarning}. This is likely caused by the value changing from ${undefinedWarning} value, which should not happen.
Decide between using a controlled or uncontrolled input element for the lifetime of the component.
More info: https://reactjs.org/link/controlled-components
${error.stack}`);
}
}, [
isControlled,
controlledValue
]);
}
return isControlled;
};