Skip to content

Commit 5ce66e2

Browse files
authored
feat: Add an option to provide serialized initial state (#378)
1 parent c0e8126 commit 5ce66e2

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

core/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,46 @@ export default function App() {
277277
}
278278
```
279279

280+
## Use `initialState` to restore state from JSON-serialized representation
281+
282+
CodeMirror allows to serialize editor state to JSON representation with [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) function for persistency or other needs. This JSON representation can be later used to recreate ReactCodeMirror component with the same internal state.
283+
284+
For example, this is how undo history can be saved in the local storage, so that it remains after the page reloads
285+
286+
```
287+
import CodeMirror from '@uiw/react-codemirror'
288+
import { historyField } from '@codemirror/commands'
289+
290+
// When custom fields should be serialized, you can pass them in as an object mapping property names to fields.
291+
// See [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) documentation for more details
292+
const stateFields = { history: historyField }
293+
294+
export function EditorWithInitialState() {
295+
const serializedState = localStorage.getItem('myEditorState')
296+
const value = localStorage.getItem('myValue') || ''
297+
298+
return (
299+
<CodeMirror
300+
value={value}
301+
initialState={
302+
serializedState
303+
? {
304+
json: JSON.parse(serializedState || ''),
305+
fields: stateFields,
306+
}
307+
: undefined
308+
}
309+
onChange={(value, viewUpdate) => {
310+
localStorage.setItem('myValue', value)
311+
312+
const state = viewUpdate.state.toJSON(stateFields)
313+
localStorage.setItem('myEditorState', JSON.stringify(state))
314+
}}
315+
/>
316+
)
317+
}
318+
```
319+
280320
## Props
281321

282322
<!--rehype:style=background-color: #ffe564; display: inline-block; border-bottom: 0; padding: 3px 12px;-->
@@ -364,6 +404,13 @@ export interface ReactCodeMirrorProps
364404
* Originally from the [config of EditorView](https://codemirror.net/6/docs/ref/#view.EditorView.constructor%5Econfig.root)
365405
*/
366406
root?: ShadowRoot | Document;
407+
/**
408+
* Create a state from its JSON representation serialized with [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) function
409+
*/
410+
initialState?: {
411+
json: any;
412+
fields?: Record<'string', StateField<any>>;
413+
};
367414
}
368415
export interface ReactCodeMirrorRef {
369416
editor?: HTMLDivElement | null;

core/src/index.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useRef, forwardRef, useImperativeHandle } from 'react';
2-
import { EditorState, EditorStateConfig, Extension } from '@codemirror/state';
2+
import { EditorState, EditorStateConfig, Extension, StateField } from '@codemirror/state';
33
import { EditorView, ViewUpdate } from '@codemirror/view';
44
import { BasicSetupOptions } from '@uiw/codemirror-extensions-basic-setup';
55
import { useCodeMirror } from './useCodeMirror';
@@ -69,6 +69,13 @@ export interface ReactCodeMirrorProps
6969
* Originally from the [config of EditorView](https://codemirror.net/6/docs/ref/#view.EditorView.constructor%5Econfig.root)
7070
*/
7171
root?: ShadowRoot | Document;
72+
/**
73+
* Create a state from its JSON representation serialized with [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) function
74+
*/
75+
initialState?: {
76+
json: any;
77+
fields?: Record<'string', StateField<any>>;
78+
};
7279
}
7380

7481
export interface ReactCodeMirrorRef {
@@ -101,6 +108,7 @@ const ReactCodeMirror = forwardRef<ReactCodeMirrorRef, ReactCodeMirrorProps>((pr
101108
editable,
102109
readOnly,
103110
root,
111+
initialState,
104112
...other
105113
} = props;
106114
const editor = useRef<HTMLDivElement>(null);
@@ -127,6 +135,7 @@ const ReactCodeMirror = forwardRef<ReactCodeMirrorRef, ReactCodeMirrorProps>((pr
127135
onCreateEditor,
128136
onUpdate,
129137
extensions,
138+
initialState,
130139
});
131140

132141
useImperativeHandle(ref, () => ({ editor: editor.current, state: state, view: view }), [

core/src/useCodeMirror.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export function useCodeMirror(props: UseCodeMirror) {
3434
indentWithTab: defaultIndentWithTab = true,
3535
basicSetup: defaultBasicSetup = true,
3636
root,
37+
initialState,
3738
} = props;
3839
const [container, setContainer] = useState<HTMLDivElement>();
3940
const [view, setView] = useState<EditorView>();
@@ -109,11 +110,14 @@ export function useCodeMirror(props: UseCodeMirror) {
109110

110111
useEffect(() => {
111112
if (container && !state) {
112-
const stateCurrent = EditorState.create({
113+
const config = {
113114
doc: value,
114115
selection,
115116
extensions: getExtensions,
116-
});
117+
};
118+
const stateCurrent = initialState
119+
? EditorState.fromJSON(initialState.json, config, initialState.fields)
120+
: EditorState.create(config);
117121
setState(stateCurrent);
118122
if (!view) {
119123
const viewCurrent = new EditorView({

0 commit comments

Comments
 (0)