Repository: excalidraw/excalidraw
File: src/components/App.tsx (TypeScript)

Code before:
          ret?.pointerDownState.segmentMidpoint.value !== null &&
          !ret?.pointerDownState.segmentMidpoint.added
        ) {
          return;
        }

        if (ret?.didAddPoint) {
          this.setState({
            selectedLinearElement: {
              ...this.state.selectedLinearElement,
              pointerDownState: ret.pointerDownState,
              selectedPointsIndices: ret.selectedPointsIndices,
            },
          });

          if (this.state.editingLinearElement) {
            this.setState({
              editingLinearElement: {
                ...this.state.editingLinearElement,
                pointerDownState: ret.pointerDownState,
                selectedPointsIndices: ret.selectedPointsIndices,
              },
            });
          }
          return;
        }

        const didDrag = LinearElementEditor.handlePointDragging(
          event,
          this.state,
          pointerCoords.x,
          pointerCoords.y,
          (element, pointsSceneCoords) => {
            this.maybeSuggestBindingsForLinearElementAtCoords(
              element,
              pointsSceneCoords,
            );
          },
          linearElementEditor,
        );
        if (didDrag) {
          pointerDownState.lastCoords.x = pointerCoords.x;
          pointerDownState.lastCoords.y = pointerCoords.y;
          pointerDownState.drag.hasOccurred = true;
          if (
            this.state.editingLinearElement &&
            !this.state.editingLinearElement.isDragging
          ) {
            this.setState({
              editingLinearElement: {
                ...this.state.editingLinearElement,

Review comment (bug):
The `LinearElementEditor.pointerDownState.segmentMidpoint` should be part of global `pointerDownState`, or possibly the whole `LinearElementEditor.pointerDownState` should be. Because if we're updating `LinearElementEditor.pointerDownState` in a way that requires it to be saved back to `appState` (instead of mutating it), there's a race condition unless we `flushSync` it everywhere we update the linear editor — but even then I'm sure it'd be error prone.

The whole flow we're handling state during pointerdown/pointermove is super brittle and subject to race conditions right now. This manifests in things like:

![excal-midpoint-creation-loop](https://user-images.githubusercontent.com/5153846/204149460-2bdd463c-5bf3-48e2-a68b-628427330f82.gif)

Wrapping these calls into `flushSync` seems to fix it, but there are many other places where we do not set (linear editor) state synchronously, so those places may be subject to race conditions as well.

Code after:
            this.state,
          )
        ) {
          const ret = LinearElementEditor.addMidpoint(
            this.state.selectedLinearElement,
            pointerCoords,
            this.state,
          );
          if (!ret) {
            return;
          }

          // Since we are reading from previous state which is not possible with
          // automatic batching in React 18 hence using flush sync to synchronously
          // update the state. Check https://github.com/excalidraw/excalidraw/pull/5508 for more details.

          flushSync(() => {
            if (this.state.selectedLinearElement) {
              this.setState({
                selectedLinearElement: {
                  ...this.state.selectedLinearElement,
                  pointerDownState: ret.pointerDownState,
                  selectedPointsIndices: ret.selectedPointsIndices,
                },
              });
            }
            if (this.state.editingLinearElement) {
              this.setState({
                editingLinearElement: {
                  ...this.state.editingLinearElement,
                  pointerDownState: ret.pointerDownState,
                  selectedPointsIndices: ret.selectedPointsIndices,
                },
              });
            }
          });

          return;
        } else if (
          linearElementEditor.pointerDownState.segmentMidpoint.value !== null &&
          !linearElementEditor.pointerDownState.segmentMidpoint.added
        ) {
          return;
        }

        const didDrag = LinearElementEditor.handlePointDragging(
          event,
          this.state,
          pointerCoords.x,
          pointerCoords.y,
          (element, pointsSceneCoords) => {