diff --git a/packages/react/__tests__/components.test.tsx b/packages/react/__tests__/components.test.tsx index 6203e622..85cdd8ae 100644 --- a/packages/react/__tests__/components.test.tsx +++ b/packages/react/__tests__/components.test.tsx @@ -5,6 +5,7 @@ import LaTeX2HTML5 from 'latex2js'; import nicebox from '../src/components/nicebox'; import enumerate from '../src/components/enumerate'; import math from '../src/components/math'; +import { LaTeX } from '../src'; describe('nicebox', () => { it('renders the parser-provided lines, not children', () => { @@ -82,3 +83,29 @@ In DFS, $\omega_0 = 2\pi/p$. expect(html).toContain('In DFS'); }); }); + +describe('LaTeX', () => { + it('reuses the parsed document when content is unchanged', () => { + const source = String.raw`\begin{nicebox}content\end{nicebox}`; + const instance = new LaTeX({ content: source }); + (instance as any).state = { mathJaxLoaded: true }; + const parse = jest.spyOn(LaTeX2HTML5.prototype, 'parse'); + + instance.render(); + instance.render(); + + expect(parse).toHaveBeenCalledTimes(1); + parse.mockRestore(); + }); + + it('typesets after an update when MathJax is loaded', () => { + const instance = new LaTeX({ content: 'content' }); + (instance as any).state = { mathJaxLoaded: true }; + const typesetMath = jest.spyOn(instance, 'typesetMath').mockImplementation(() => {}); + + instance.componentDidUpdate(); + + expect(typesetMath).toHaveBeenCalledTimes(1); + typesetMath.mockRestore(); + }); +}); diff --git a/packages/react/src/index.tsx b/packages/react/src/index.tsx index c6dcf46f..2ef1ced5 100644 --- a/packages/react/src/index.tsx +++ b/packages/react/src/index.tsx @@ -28,6 +28,9 @@ interface LaTeXState { export class LaTeX extends Component { private containerRef = React.createRef(); + private parsedContent: string | null = null; + private parsed: any = null; + private children: React.ReactElement[] = []; constructor(props: LaTeXProps) { super(props); @@ -38,14 +41,12 @@ export class LaTeX extends Component { componentDidMount() { loadMathJax(() => { - this.setState({ mathJaxLoaded: true }, () => { - this.typesetMath(); - }); + this.setState({ mathJaxLoaded: true }); }); } - componentDidUpdate(prevProps: LaTeXProps) { - if (prevProps.content !== this.props.content && this.state.mathJaxLoaded) { + componentDidUpdate() { + if (this.state.mathJaxLoaded) { this.typesetMath(); } } @@ -64,20 +65,22 @@ export class LaTeX extends Component { return
Loading...
; } - const latex = new LaTeX2HTML5(); - const parsed = latex.parse(this.props.content); - - const children: React.ReactElement[] = []; - - parsed && - parsed.forEach && - parsed.forEach((el: any) => { - if (ELEMENTS.hasOwnProperty(el.type)) { - const elementType = el.type as keyof typeof ELEMENTS; - const Component = ELEMENTS[elementType]; - children.push(createElement(Component as any, { ...el, key: children.length })); - } - }); + if (this.parsedContent !== this.props.content) { + const latex = new LaTeX2HTML5(); + this.parsed = latex.parse(this.props.content); + this.parsedContent = this.props.content; + + this.children = []; + this.parsed && + this.parsed.forEach && + this.parsed.forEach((el: any) => { + if (ELEMENTS.hasOwnProperty(el.type)) { + const elementType = el.type as keyof typeof ELEMENTS; + const Component = ELEMENTS[elementType]; + this.children.push(createElement(Component as any, { ...el, key: this.children.length })); + } + }); + } return (
@@ -86,7 +89,7 @@ export class LaTeX extends Component { package defines them before any math that uses them — the same hidden-div approach the html5 and vue renderers use. */}
{macroStr}
- {children} + {this.children}
); }