react-dom#flushSync TypeScript Examples

The following examples show how to use react-dom#flushSync. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ui-jsx.test-utils.tsx    From utopia with MIT License 5 votes vote down vote up
export async function renderTestEditorWithModel(
  model: PersistentModel,
  awaitFirstDomReport: 'await-first-dom-report' | 'dont-await-first-dom-report',
  mockBuiltInDependencies?: BuiltInDependencies,
): Promise<{
  dispatch: (actions: ReadonlyArray<EditorAction>, waitForDOMReport: boolean) => Promise<void>
  getDispatchFollowUpActionsFinished: () => Promise<void>
  getEditorState: () => EditorStorePatched
  renderedDOM: RenderResult
  getNumberOfCommits: () => number
  getNumberOfRenders: () => number
  clearRecordedActions: () => void
  getRecordedActions: () => ReadonlyArray<EditorAction>
}> {
  const renderCountBaseline = renderCount
  let recordedActions: Array<EditorAction> = []

  let emptyEditorState = createEditorState(NO_OP)
  const derivedState = deriveState(emptyEditorState, null)

  const history = History.init(emptyEditorState, derivedState)

  let editorDispatchPromises: Array<Promise<void>> = []
  async function getDispatchFollowUpActionsFinished(): Promise<void> {
    return Promise.all(editorDispatchPromises).then(NO_OP)
  }

  let workingEditorState: EditorStoreFull

  const spyCollector = emptyUiJsxCanvasContextData()

  // Reset canvas globals
  resetDispatchGlobals()
  clearAllRegisteredControls()
  clearListOfEvaluatedFiles()

  const asyncTestDispatch = async (
    actions: ReadonlyArray<EditorAction>,
    priority?: DispatchPriority, // priority is not used in the editorDispatch now, but we didn't delete this param yet
    waitForDispatchEntireUpdate = false,
  ) => {
    recordedActions.push(...actions)
    const result = editorDispatch(asyncTestDispatch, actions, workingEditorState, spyCollector)
    editorDispatchPromises.push(result.entireUpdateFinished)
    invalidateDomWalkerIfNecessary(
      domWalkerMutableState,
      workingEditorState.patchedEditor,
      result.patchedEditor,
    )

    workingEditorState = result
    if (waitForDispatchEntireUpdate) {
      await Utils.timeLimitPromise(
        getDispatchFollowUpActionsFinished(),
        2000,
        'Follow up actions took too long.',
      )
    }

    flushSync(() => {
      canvasStoreHook.setState(patchedStoreFromFullStore(workingEditorState))
    })

    // run dom walker

    const domWalkerResult = runDomWalker({
      domWalkerMutableState: domWalkerMutableState,
      selectedViews: workingEditorState.patchedEditor.selectedViews,
      elementsToFocusOn: workingEditorState.patchedEditor.canvas.elementsToRerender,
      scale: workingEditorState.patchedEditor.canvas.scale,
      additionalElementsToUpdate:
        workingEditorState.patchedEditor.canvas.domWalkerAdditionalElementsToUpdate,
      rootMetadataInStateRef: {
        current: Object.values(workingEditorState.patchedEditor.domMetadata),
      },
    })

    if (domWalkerResult != null) {
      const saveDomReportAction = saveDOMReport(
        domWalkerResult.metadata,
        domWalkerResult.cachedPaths,
        domWalkerResult.invalidatedPaths,
      )
      recordedActions.push(saveDomReportAction)
      const editorWithNewMetadata = editorDispatch(
        asyncTestDispatch,
        [saveDomReportAction],
        workingEditorState,
        spyCollector,
      )
      workingEditorState = editorWithNewMetadata
    }

    // update state with new metadata

    flushSync(() => {
      storeHook.setState(patchedStoreFromFullStore(workingEditorState))
      if (shouldInspectorUpdate(workingEditorState.strategyState)) {
        inspectorStoreHook.setState(patchedStoreFromFullStore(workingEditorState))
      }
    })
  }

  const workers = new UtopiaTsWorkersImplementation(
    new FakeParserPrinterWorker(),
    new FakeLinterWorker(),
    new FakeWatchdogWorker(),
  )

  const builtInDependencies =
    mockBuiltInDependencies != null
      ? mockBuiltInDependencies
      : createBuiltInDependenciesList(workers)
  const initialEditorStore: EditorStoreFull = {
    strategyState: createEmptyStrategyState({}, {}),
    unpatchedEditor: emptyEditorState,
    patchedEditor: emptyEditorState,
    unpatchedDerived: derivedState,
    patchedDerived: derivedState,
    history: history,
    userState: {
      loginState: notLoggedIn,
      shortcutConfig: {},
    },
    workers: workers,
    persistence: DummyPersistenceMachine,
    dispatch: asyncTestDispatch,
    alreadySaved: false,
    builtInDependencies: builtInDependencies,
  }

  const canvasStoreHook = create<
    EditorStorePatched,
    SetState<EditorStorePatched>,
    GetState<EditorStorePatched>,
    Mutate<StoreApi<EditorStorePatched>, [['zustand/subscribeWithSelector', never]]>
  >(subscribeWithSelector((set) => patchedStoreFromFullStore(initialEditorStore)))

  const domWalkerMutableState = createDomWalkerMutableState(canvasStoreHook)

  const inspectorStoreHook = create<
    EditorStorePatched,
    SetState<EditorStorePatched>,
    GetState<EditorStorePatched>,
    Mutate<StoreApi<EditorStorePatched>, [['zustand/subscribeWithSelector', never]]>
  >(subscribeWithSelector((set) => patchedStoreFromFullStore(initialEditorStore)))

  const storeHook = create<
    EditorStorePatched,
    SetState<EditorStorePatched>,
    GetState<EditorStorePatched>,
    Mutate<StoreApi<EditorStorePatched>, [['zustand/subscribeWithSelector', never]]>
  >(subscribeWithSelector((set) => patchedStoreFromFullStore(initialEditorStore)))

  // initializing the local editor state
  workingEditorState = initialEditorStore

  let numberOfCommits = 0

  const result = render(
    <React.Profiler
      id='editor-root'
      /* eslint-disable-next-line react/jsx-no-bind */
      onRender={(id, phase, actualDuration, baseDuration, startTime, commitTime, interactions) => {
        numberOfCommits++
      }}
    >
      <FailJestOnCanvasError />
      <EditorRoot
        api={storeHook}
        useStore={storeHook}
        canvasStore={canvasStoreHook}
        spyCollector={spyCollector}
        inspectorStore={inspectorStoreHook}
        domWalkerMutableState={domWalkerMutableState}
      />
    </React.Profiler>,
    { legacyRoot: true },
  )

  await act(async () => {
    await new Promise<void>((resolve, reject) => {
      load(
        async (actions) => {
          try {
            await asyncTestDispatch(actions, undefined, true)
            resolve()
          } catch (e) {
            reject(e)
          }
        },
        model,
        'Test',
        '0',
        initialEditorStore.builtInDependencies,
        false,
      )
    })
  })

  await act(async () => {
    await asyncTestDispatch(
      [switchEditorMode(EditorModes.selectMode()), setPanelVisibility('codeEditor', false)],
      undefined,
      true,
    )
  })

  return {
    dispatch: async (actions: ReadonlyArray<EditorAction>, waitForDOMReport: boolean) => {
      return await act(async () => {
        await asyncTestDispatch(actions, 'everyone', true)
      })
    },
    getDispatchFollowUpActionsFinished: getDispatchFollowUpActionsFinished,
    getEditorState: () => storeHook.getState(),
    renderedDOM: result,
    getNumberOfCommits: () => numberOfCommits,
    getNumberOfRenders: () => renderCount - renderCountBaseline,
    clearRecordedActions: () => {
      recordedActions = []
    },
    getRecordedActions: () => recordedActions,
  }
}
Example #2
Source File: use-collapse.ts    From mantine with MIT License 4 votes vote down vote up
export function useCollapse({
  transitionDuration,
  transitionTimingFunction = 'ease',
  onTransitionEnd = () => {},
  opened,
}: UseCollapse): (props: GetCollapseProps) => Record<string, any> {
  const el = useRef<HTMLElement | null>(null);
  const collapsedHeight = '0px';
  const collapsedStyles = {
    display: 'none',
    height: '0px',
    overflow: 'hidden',
  };
  const [styles, setStylesRaw] = useState<React.CSSProperties>(opened ? {} : collapsedStyles);
  const setStyles = (newStyles: {} | ((oldStyles: {}) => {})): void => {
    flushSync(() => setStylesRaw(newStyles));
  };

  const mergeStyles = (newStyles: {}): void => {
    setStyles((oldStyles) => ({ ...oldStyles, ...newStyles }));
  };

  function getTransitionStyles(height: number | string): {
    transition: string;
  } {
    const _duration = transitionDuration || getAutoHeightDuration(height);
    return {
      transition: `height ${_duration}ms ${transitionTimingFunction}`,
    };
  }

  useDidUpdate(() => {
    if (opened) {
      raf(() => {
        mergeStyles({ willChange: 'height', display: 'block', overflow: 'hidden' });
        raf(() => {
          const height = getElementHeight(el);
          mergeStyles({ ...getTransitionStyles(height), height });
        });
      });
    } else {
      raf(() => {
        const height = getElementHeight(el);
        mergeStyles({ ...getTransitionStyles(height), willChange: 'height', height });
        raf(() => mergeStyles({ height: collapsedHeight, overflow: 'hidden' }));
      });
    }
  }, [opened]);

  const handleTransitionEnd = (e: React.TransitionEvent): void => {
    if (e.target !== el.current || e.propertyName !== 'height') {
      return;
    }

    if (opened) {
      const height = getElementHeight(el);

      if (height === styles.height) {
        setStyles({});
      } else {
        mergeStyles({ height });
      }

      onTransitionEnd();
    } else if (styles.height === collapsedHeight) {
      setStyles(collapsedStyles);
      onTransitionEnd();
    }
  };

  function getCollapseProps({ style = {}, refKey = 'ref', ...rest }: GetCollapseProps = {}) {
    const theirRef: any = rest[refKey];
    return {
      'aria-hidden': !opened,
      ...rest,
      [refKey]: useMergedRef(el, theirRef),
      onTransitionEnd: handleTransitionEnd,
      style: { boxSizing: 'border-box', ...style, ...styles },
    };
  }

  return getCollapseProps;
}