shakeeb
shakeeb

Reputation: 1

Open Popover upon focusing on and div with contentEditable true and close upon blur of editable box

I have written a React component using MUI Modal, Popover, and Box inside my modal there is a <Box /> which contentEditable true what I am trying to do upon focusing on box open popover which will some options, and on blur close that popover. but as soon as I focused I got the error of infinite loop. I debug and found that it is happening because of a focus shift between popover and box.

This is my code

import React, { useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { ClickAwayListener } from '@mui/material';
import {
  Modal,
  Box,
  IconButton,
  Paper,
  Popover,
  ButtonGroup,
  Button,
} from '@mui/material';
import {
  Plus,
  Bold,
  AlignLeft,
  AlignCenter,
  AlignRight,
  Type
} from 'lucide-react';
                    
interface Editor {
  id: string;
  value: string;
}
                    
interface ToolbarProps {
  anchorEl: HTMLDivElement | null;
  onClose: () => void;
  onFormat: (command: string, value?: string) => void;
}
                    
const Toolbar: React.FC<ToolbarProps> = ({ anchorEl, onClose, onFormat }) => {
  return (                      
    <Popover open={Boolean(anchorEl)} onClose={onClose}>
      <ButtonGroup variant="outlined" size="small">
        <IconButton onClick={() => onFormat("bold")}>
          <Bold size={16} />
        </IconButton>
        <IconButton onClick={() => onFormat("fontSize", "2")}>
          <Type size={14} />
        </IconButton>
        <IconButton onClick={() => onFormat("fontSize", "7")}>
          <Type size={18} />
        </IconButton>
        <IconButton onClick={() => onFormat("justifyLeft")}>
          <AlignLeft size={16} />
        </IconButton>
        <IconButton onClick={() => onFormat("justifyCenter")}>
          <AlignCenter size={16} />
        </IconButton>
        <IconButton onClick={() => onFormat("justifyRight")}>
          <AlignRight size={16} />
        </IconButton>
      </ButtonGroup>
    </Popover>
  );
};
                    
export const TextEditor: React.FC = () => {
  const [editors, setEditors] = useState<Editor[]>([]);
  const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null);
  const [activeEditorId, setActiveEditorId] = useState<string | null>(null);
                    
  const handleAddEditor = () => {
    const newEditor = {
      id: uuidv4(),
      value: '',
    };
    setEditors([...editors, newEditor]);
  };
                    
  const handleEditorChange = (id: string, value: string) => {
    setEditors(editors.map(editor => 
      editor.id === id ? { ...editor, value } : editor
    ));
  };
                    
  const handleEditorFocus = (event: React.FocusEvent<HTMLDivElement>, id: string) => {
    setAnchorEl(event.currentTarget);
    setActiveEditorId(id);
  };
                    
  const handleEditorBlur = (event: React.FocusEvent<HTMLDivElement>) => {
    setAnchorEl(null);
    setActiveEditorId(null);
  }
                    
  const handleFormat = (command: string, value?: string) => {
    document.execCommand(command, false, value);
  };
                    
  return (
    <Modal
      open={true}
      aria-labelledby="text-editor-modal"
      className="flex items-center justify-center"
    >
      <Box className="bg-white rounded-lg p-6 w-[600px] max-h-[80vh] overflow-y-auto">
        <div className="flex justify-between items-center mb-4">
          <h2 className="text-xl font-semibold">Text Editor</h2>
          <IconButton onClick={handleAddEditor} color="primary">
            <Plus />
          </IconButton>
        </div>
                    
        <div className="space-y-4">
          {editors.map((editor) => (
            <Box
              key={editor.id}
              contentEditable
              onFocus={(e) => handleEditorFocus(e, editor.id)}
              onBlur={handleEditorBlur}
              onInput={(e) => handleEditorChange(editor.id, e.currentTarget.innerHTML)}
              className="min-h-[100px] p-4 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
              dangerouslySetInnerHTML={{ __html: editor.value }}
            />
          ))}
        </div>
                    
        <Toolbar
          anchorEl={anchorEl}
          onClose={() => setAnchorEl(null)}
          onFormat={handleFormat}
        />
      </Box>
    </Modal>
  );
};

This is my getting in dev console:

[email protected]:26 The above error occurred in the <Transition2> component:

    at Transition2 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:13089:30)
    at Fade2 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:23588:17)
    at Backdrop2 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:23809:17)
    at https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…ntainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:1691:50
    at div
    at https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…ntainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:1691:50
    at Portal2 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:19751:15)
    at Modal2 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:28248:17)
    at https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…ntainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:1691:50
    at Popover2 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:36682:17)
    at Toolbar (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentialless.webcontainer-api.io/src/components/TextEditor.tsx:35:20)
    at div
    at https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…ntainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:1691:50
    at Box4 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…ntainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:7990:19)
    at FocusTrap (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:27815:15)
    at div
    at https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…ntainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:1691:50
    at Portal2 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:19751:15)
    at Modal2 (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentia…tainer-api.io/node_modules/.vite/deps/@mui_material.js?v=482cf364:28248:17)
    at TextEditor (https://zp1v56uxy8rdx5ypatb0ockcb9tr6a-oci3--5173--c8c182a3.local-credentialless.webcontainer-api.io/src/components/TextEditor.tsx:104:33)
    at div
    at App

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
chunk-6VWAHX6D.js?v=15727a7e:19659 Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    at checkForNestedUpdates (chunk-6VWAHX6D.js?v=15727a7e:19659:19)
    at scheduleUpdateOnFiber (chunk-6VWAHX6D.js?v=15727a7e:18533:11)
    at Object.enqueueSetState (chunk-6VWAHX6D.js?v=15727a7e:13467:15)
    at Component.setState (chunk-QJTFJ6OV.js?v=15727a7e:229:24)
    at Transition2.safeSetState (@mui_material.js?v=482cf364:13246:10)
    at Transition2.performEnter (@mui_material.js?v=482cf364:13198:10)
    at Transition2.updateStatus (@mui_material.js?v=482cf364:13172:14)
    at Transition2.componentDidUpdate (@mui_material.js?v=482cf364:13141:10)
    at commitLayoutEffectOnFiber (chunk-6VWAHX6D.js?v=15727a7e:17053:36)
    at commitLayoutMountEffects_complete (chunk-6VWAHX6D.js?v=15727a7e:17980:17)
checkForNestedUpdates   @   chunk-6VWAHX6D.js?v=15727a7e:19659
scheduleUpdateOnFiber   @   chunk-6VWAHX6D.js?v=15727a7e:18533
enqueueSetState @   chunk-6VWAHX6D.js?v=15727a7e:13467
Component.setState  @   chunk-QJTFJ6OV.js?v=15727a7e:229
safeSetState    @   @mui_material.js?v=482cf364:13246
performEnter    @   @mui_material.js?v=482cf364:13198
updateStatus    @   @mui_material.js?v=482cf364:13172
componentDidUpdate  @   @mui_material.js?v=482cf364:13141
commitLayoutEffectOnFiber   @   chunk-6VWAHX6D.js?v=15727a7e:17053
commitLayoutMountEffects_complete   @   chunk-6VWAHX6D.js?v=15727a7e:17980
commitLayoutEffects_begin   @   chunk-6VWAHX6D.js?v=15727a7e:17969
commitLayoutEffects @   chunk-6VWAHX6D.js?v=15727a7e:17920
commitRootImpl  @   chunk-6VWAHX6D.js?v=15727a7e:19353
commitRoot  @   chunk-6VWAHX6D.js?v=15727a7e:19277
performSyncWorkOnRoot   @   chunk-6VWAHX6D.js?v=15727a7e:18895
flushSyncCallbacks  @   chunk-6VWAHX6D.js?v=15727a7e:9119
(anonymous) @   chunk-6VWAHX6D.js?v=15727a7e:18627

Upvotes: 0

Views: 16

Answers (0)

Related Questions