'use client'; import { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; import * as React from 'react'; /** * @internal * This hook returns context selected value by selector. * It will only accept context created by `createContext`. * It will trigger re-render if only the selected value is referentially changed. */ export const useContextSelector = (context, selector)=>{ const contextValue = React.useContext(context); const { value: { current: value }, version: { current: version }, listeners } = contextValue; const selected = selector(value); const [state, setState] = React.useState([ value, selected ]); const dispatch = (payload)=>{ setState((prevState)=>{ if (!payload) { // early bail out when is dispatched during render return [ value, selected ]; } if (payload[0] <= version) { if (Object.is(prevState[1], selected)) { return prevState; // bail out } return [ value, selected ]; } try { if (Object.is(prevState[0], payload[1])) { return prevState; // do not update } const nextSelected = selector(payload[1]); if (Object.is(prevState[1], nextSelected)) { return prevState; // do not update } return [ payload[1], nextSelected ]; } catch (e) { // ignored (stale props or some other reason) } // explicitly spread to enforce typing return [ prevState[0], prevState[1] ]; // schedule update }); }; if (!Object.is(state[1], selected)) { // schedule re-render // this is safe because it's self contained dispatch(undefined); } const stableDispatch = useEventCallback(dispatch); useIsomorphicLayoutEffect(()=>{ listeners.push(stableDispatch); return ()=>{ const index = listeners.indexOf(stableDispatch); listeners.splice(index, 1); }; }, [ stableDispatch, listeners ]); return state[1]; };