diff --git a/change/@fluentui-react-utilities-fa9ce15a-c1d5-4b9a-93e7-e07b2d60b837.json b/change/@fluentui-react-utilities-fa9ce15a-c1d5-4b9a-93e7-e07b2d60b837.json new file mode 100644 index 00000000000000..0eb8f7bc317d7a --- /dev/null +++ b/change/@fluentui-react-utilities-fa9ce15a-c1d5-4b9a-93e7-e07b2d60b837.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: simplifies useControllableState hook internals", + "packageName": "@fluentui/react-utilities", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-utilities/src/hooks/useControllableState.ts b/packages/react-components/react-utilities/src/hooks/useControllableState.ts index 3cb467a3cbdcd9..4bb5f59b588047 100644 --- a/packages/react-components/react-utilities/src/hooks/useControllableState.ts +++ b/packages/react-components/react-utilities/src/hooks/useControllableState.ts @@ -19,10 +19,6 @@ export type UseControllableStateOptions = { initialState: State; }; -function isFactoryDispatch(newState: React.SetStateAction): newState is (prevState: State) => State { - return typeof newState === 'function'; -} - /** * @internal * @@ -42,40 +38,21 @@ function isFactoryDispatch(newState: React.SetStateAction): newSta export const useControllableState = ( options: UseControllableStateOptions, ): [State, React.Dispatch>] => { - const isControlled = useIsControlled(options.state); const initialState = typeof options.defaultState === 'undefined' ? options.initialState : options.defaultState; const [internalState, setInternalState] = React.useState(initialState); - - const state = isControlled ? (options.state as State) : internalState; - - const stateRef = React.useRef(state); - React.useEffect(() => { - stateRef.current = state; - }, [state]); - - // To match the behavior of the setter returned by React.useState, this callback's identity - // should never change. This means it MUST NOT directly reference variables that can change. - const setState = React.useCallback((newState: React.SetStateAction) => { - // React dispatch can use a factory - // https://reactjs.org/docs/hooks-reference.html#functional-updates - if (isFactoryDispatch(newState)) { - stateRef.current = newState(stateRef.current); - } else { - stateRef.current = newState; - } - - setInternalState(stateRef.current); - }, []); - - return [state, setState]; + return useIsControlled(options.state) ? [options.state, noop] : [internalState, setInternalState]; }; +function noop() { + /* noop */ +} + /** * 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: unknown) => { +const useIsControlled = (controlledValue?: V): controlledValue is V => { const [isControlled] = React.useState(() => controlledValue !== undefined); if (process.env.NODE_ENV !== 'production') {