react-codemirror
react-codemirror copied to clipboard
Code examples cause lots of re-renders due to redefintions of `extensions` and `onChange`
The following code example in the ReadMe will cause a lot of unnecessary re-renders, because of prop changes.
import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';
export default function App() {
return (
<CodeMirror
value="console.log('hello world!');"
height="200px"
extensions={[javascript({ jsx: true })]}
onChange={(value, viewUpdate) => {
console.log('value:', value);
}}
/>
);
}
Instead, arrays and objects should be cached, avoiding the need to re-render or at least reducing the need for DOM changes (e.g. dispatching events, updating event handlers).
- Define
extensionsoutside ofApp()or useuseMemo()to avoid unnecessary redefinitions. - Wrap the onChange handler in
useCallback(… , [])to indicate that it doesn't change.
import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';
// Define the extensions outside the component so the array and objects inside stay the same on every re-render.
const extensions = [javascript({ jsx: true })];
export default function App() {
return (
<CodeMirror
value="console.log('hello world!');"
height="200px"
extensions={extensions}
onChange={useCallback((value, viewUpdate) => {
console.log('value:', value);
}, [])}
/>
);
}
This is likely one of the reasons why the component has been reported to be slow in #294.
@jaller94 👍 thx!
Note: if you're not passing any extensions, it goes back to being slow. Even if you don't want to add any extensions, you should still declare an empty const extensions = [] outside your component and pass it to CodeMirror.
And as @jaller94 pointed out, you will have to do both - use useCallback for onChange and have a global extensions array.
Additionally, the demo posted with this repo https://uiwjs.github.io/react-codemirror/ suffers from the slowdown. Try holding backspace in the codemirror window in the demo - the code gets deleted in chunks instead of in a smooth letter by letter fashion.
@jaywcjlove any possibility of having @jaller94 's solution as the default behavior? Thanks :)
@jaller94 Upgrade v4.11.5
@jaller94 Upgrade
v4.11.5
I see that you've added a useCallback wrapper in the new version but that doesn't fix it for me.
I've upgraded to "@uiw/react-codemirror": "^4.11.5" but I still have to wrap the onChange with a useCallback myself to keep the performance.
@AgarwalPragy How should I handle this? PRs are welcome.
@jaywcjlove
- I recommend to revert your change as described in #373.
- Here is the PR you asked for: #372
It's not your component code that's inefficient. It's the code example in the ReadMe. If you're unsure why, I recommend reading: https://www.debugbear.com/blog/react-rerenders#passing-objects-as-props