Hello everyone,
I’m facing an issue with what seems to be multiple updates or re-renders, and I can’t figure out the cause. My goal is to display an editor preloaded with initial data (in my case, SQL) and format that data using the Prettier extension. While the initial data is loaded and formatted as expected, it then reverts back to its original, unformatted state.
I debugged the process and confirmed that the data is being formatted correctly, but some update occurs afterward, causing it to revert to the previous unformatted data. I suspect this might be due to React’s internal behavior or perhaps something specific to CodeMirror.
Since I’m new to both React and CodeMirror, I would greatly appreciate any help or guidance on resolving this issue.
import CodeMirror, { EditorView, ViewUpdate } from '@uiw/react-codemirror';
import { createTheme } from '@uiw/codemirror-themes';
import { tags as t } from '@lezer/highlight';
import { useCallback, useRef } from 'react';
import { sql } from '@codemirror/lang-sql';
import { vscodeDark } from '@uiw/codemirror-theme-vscode';
import { UseBaseStore } from '@store';
import { LoadingOutlined } from '@ant-design/icons';
import { useRunSqlQuery, useRunTextQuery } from '@hooks';
import { Button } from 'antd';
import prettier from 'prettier';
import sqlFormatter from 'prettier-plugin-sql';
const myTheme = createTheme({
theme: 'dark',
settings: {
background: '#30294A',
// backgroundImage: '',
foreground: '#FFFFFF',
// caret: '#5d00ff',
// selection: '#036dd626',
// selectionMatch: '#036dd626',
// lineHighlight: '#8a91991a',
gutterBackground: '#30294A',
gutterForeground: '#FFFFFF',
},
styles: [
// { tag: t.comment, color: '#787b8099' },
// { tag: t.variableName, color: '#0080ff' },
// { tag: [t.string, t.special(t.brace)], color: '#5c6166' },
// { tag: t.number, color: '#5c6166' },
// { tag: t.bool, color: '#5c6166' },
// { tag: t.null, color: '#5c6166' },
{ tag: t.keyword, color: '#7eb9eb' },
// { tag: t.operator, color: '#5c6166' },
// { tag: t.className, color: '#5c6166' },
// { tag: t.definition(t.typeName), color: '#5c6166' },
// { tag: t.typeName, color: '#5c6166' },
// { tag: t.angleBracket, color: '#5c6166' },
// { tag: t.tagName, color: '#5c6166' },
// { tag: t.attributeName, color: '#5c6166' },
],
});
const myThemeCustomStyles = EditorView.theme({
'&': {
fontSize: '16px',
borderRadius: '12px',
},
'.cm-scroller': {
borderRadius: '12px',
scrollbarWidth: 'none',
},
'.cm-scroller::-webkit-scrollbar': {
display: 'none',
},
});
const extensions = [sql(), myThemeCustomStyles, vscodeDark, EditorView.lineWrapping];
const formatSql = (sql: string) => {
return prettier.format(sql, {
parser: 'sql',
plugins: [sqlFormatter],
});
};
export default function SxSqlPrompt() {
const editorRef = useRef<EditorView | null>(null);
const setSqlPrompt = UseBaseStore.use.setSqlPrompt();
const handleRunClick = useCallback(() => {
const editor = editorRef.current;
if (editor) {
const value = editor.state.doc.toString();
if (value.trim() !== '') {
setSqlPrompt(value); //this migth not be stored in store since it is use only in this componenet (make sure to handle switch between text and sql prompt)
}
}
}, []);
const handleClearClick = useCallback(() => {
const editor = editorRef.current;
if (editor) {
const transaction = editor.state.update({
changes: { from: 0, to: editor.state.doc.length, insert: '' },
});
editor.dispatch(transaction);
}
}, []);
const handleFormatClick = useCallback(() => {
const editor = editorRef.current;
if (editor) {
const value = editor.state.doc.toString();
if (value.trim() !== '') {
formatSql(value).then((formattedValue) => {
const transaction = editor.state.update({
changes: { from: 0, to: editor.state.doc.length, insert: formattedValue },
});
editor.dispatch(transaction);
});
}
}
}, []);
const { isFetching: isFetchingSql } = useRunSqlQuery();
const { data: textResult } = useRunTextQuery();
return (
<div className="relative">
{isFetchingSql && (
<div className="w-full mb-2 text-[#F2F0FE] absolute top-[320px] left-[40px] z-10">
Searching Datasets...
<LoadingOutlined className="ml-2" />
</div>
)}
<CodeMirror
onCreateEditor={async (editor) => {
handleFormatClick();
handleRunClick();
}}
theme={myTheme}
height="350px"
value={textResult?.sql}
extensions={extensions}
editable={!isFetchingSql}
/>
<div className="flex justify-end mt-2 space-x-2">
<Button onClick={handleRunClick} disabled={isFetchingSql}>
Run SQL
</Button>
<Button onClick={handleFormatClick} disabled={isFetchingSql}>
Format SQL
</Button>
<Button onClick={handleClearClick} disabled={isFetchingSql}>
Clear Editor
</Button>
</div>
</div>
);
}