Ionware
Ionware

Reputation: 48

Rendering Math in React Component

I am trying to Render Math Equation in a React Component with MathJax. It actually worked well if I pre-render it within the HTML file, but it is a mess when i try rendering it in React.

Here's my code

class Latex extends React.Component {

constructor(props) {
    super(props);
}

componentDidMount(){
    MathJax.Hub.Queue(['Typeset', MathJax.Hub, ReactDOM.findDOMNode(this)]);
}

componentDidUpdate() {
    MathJax.Hub.Queue(['Typeset', MathJax.Hub, ReactDOM.findDOMNode(this)]);
}

render() {
    //dangerouslySetInnerHTML={{__html: this.props.children}}
    return (
         <h5 dangerouslySetInnerHTML={{__html: this.props.children}}></h5>
    );
}
}

Math Display both in Regular HTML and React Element

Upvotes: 3

Views: 19831

Answers (5)

kibble
kibble

Reputation: 46

Been a couple years, but if anyone is looking for a functional + mathjax3 based approach there is this repo:

https://github.com/jpribyl/react-hook-mathjax

demo of it is available at https://johnpribyl.com/react-hook-mathjax/

Usage is like this:

Basic inline display

import React from "react";
import Tex2SVG from "react-hook-mathjax";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Tex2SVG display="inline" latex="e^{i \pi} + 1 = 0" />
      </header>
    </div>
  );
}

export default App;

Customizing MathJax options

import React from "react";
import Tex2SVG, { MathJaxProvider } from "react-hook-mathjax";

// This object contains the default options, more info at:
// http://docs.mathjax.org/en/latest/options/output/svg.html 
const mathJaxOptions = {
  svg: {
    scale: 1,                      // global scaling factor for all expressions
    minScale: .5,                  // smallest scaling factor to use
    mtextInheritFont: false,       // true to make mtext elements use surrounding font
    merrorInheritFont: true,       // true to make merror text use surrounding font
    mathmlSpacing: false,          // true for MathML spacing rules, false for TeX rules
    skipAttributes: {},            // RFDa and other attributes NOT to copy to the output
    exFactor: .5,                  // default size of ex in em units
    displayAlign: 'center',        // default for indentalign when set to 'auto'
    displayIndent: '0',            // default for indentshift when set to 'auto'
    fontCache: 'local',            // or 'global' or 'none'
    localID: null,                 // ID to use for local font cache (for single equation processing)
    internalSpeechTitles: true,    // insert <title> tags with speech content
    titleID: 0                     // initial id number to use for aria-labeledby titles
  }
}

function App() {
  return (
    <div>
      <MathJaxProvider options={mathJaxOptions} />
      <div className="App">
        <header className="App-header">
          <Tex2SVG display="inline" latex="e^{i \pi} + 1 = 0" />
        </header>
      </div>
    </div>
  );
}

export default App;

Parsing user input

import React from "react";
import Tex2SVG from "react-hook-mathjax";

function App() {
  const [inputValue, setInputValue] = React.useState(
    "G_{\\mu\\nu} + \\Lambda g_{\\mu\\nu} = \\kappa T_{\\mu\\nu}",
  );

  return (
    <div className="App">
      <header className="App-header">
        <h3>React Hook MathJax</h3>
        <input
          type="text"
          defaultValue={inputValue}
          onChange={e => setInputValue(e.target.value)}
        />

        <div className="tex-container">
          <Tex2SVG class="tex" tabindex={-1} latex={inputValue} />
        </div>
      </header>
    </div>
  );
}

export default App;

Handling error states

import React from "react";
import Tex2SVG from "react-hook-mathjax";

const getErrorFromHTML = (html) =>
  html.children[1].firstChild.firstChild.attributes["data-mjx-error"].value;

function App() {
  const [inputValue, setInputValue] = React.useState(
    "G_{\\mu\\nu} + \\Lambda g_{\\mu\\nu} = \\kappa T_{\\mu\\nu}",
  );
  const [lastValidInput, setLastValidInput] = React.useState("");
  const [error, setError] = React.useState(null);
  const hasError = error !== null;

  return (
    <div className="App">
      <header className="App-header">
        <h3>React Hook MathJax</h3>
        <input
          className={`${hasError ? "error" : ""}`}
          type="text"
          defaultValue={inputValue}
          onChange={e => {
            setInputValue(e.target.value);
            setError(null);
          }}
        />

        <div className="tex-container">
          <Tex2SVG
            class="tex"
            tabindex={-1}
            latex={hasError ? lastValidInput : inputValue}
            onSuccess={() =>
              setLastValidInput(hasError ? lastValidInput : inputValue)
            }
            onError={html => setError(getErrorFromHTML(html))}
          />
        </div>
    {hasError && <div>hint: {error}</div>}
      </header>
    </div>
  );
}

export default App;

API

MathJaxProvider props:

options Object, optional

Tex2SVG props:

latex string, required

onSuccess (HTMLElement) => void, optional

  • Triggered after a successful conversion of LaTeX string into an svg - it receives the html object generated by MathJax
  • Default: (html: HTMLElement) => {}

onError (HTMLElement) => void, optional

  • Triggered after a failed conversion of LaTeX string into an svg - it receives the html object generated by MathJax
  • Default: (html: HTMLElement) => {}

Other html attributes {[key: string]: any} optional

  • All other props passed into this component will be directly translated onto the DOM node
  • This allows you to add css classes or other handlers, see usage examples above
  • NOTE: this does not attach to the virtual DOM, so pass class - not className etc

Upvotes: 2

Tejash L
Tejash L

Reputation: 9

Add this before the Component class declare var MathJax;. It should work seamlessly.

Upvotes: 0

Ansjovis86
Ansjovis86

Reputation: 1545

I used a combination of algebra.js and react-mathjax libraries to get it working in ES6. Because all these other packages are poorly maintained. For react-mathjax please use this repo.

Here's an example for showing fractions:

import React from 'react';
import { Fraction, toTex } from 'algebra.js';
import { Node, Context } from 'react-mathjax';

function Formula(props) {
  return (
    <Context input="tex">
      <Node inline>{props.tex}</Node>
    </Context>
  );
}

export default function FractionDisplay() {
  const a = new Fraction(1, 5);
  const b = new Fraction(2, 7);
  const answer = a.multiply(b);

  const question = <Formula tex={`${toTex(a)} × ${toTex(b)} = ${toTex(answer)}`} />;

  return (
    <div>
      {question}
    </div>
  );
}

Upvotes: 3

Ionware
Ionware

Reputation: 48

Oh no, I was so disappointed in myself when I found out what I was doing wrong. I applied display of Block to the Span element that renders the Latex (My take was that it would treat the whole chunk as one) and it put each Equation character onto a separate line. Changing the display to "inline" fixed the problem.

Upvotes: 0

&#210;scar Raya
&#210;scar Raya

Reputation: 608

Your logic is good but this is not the way react works. Instead of using MathJax why you don't use react-mathjax or react-formula-beautifier. And use them just like another component (which is how react is intended to work).

Here you have the oficial example.

Advise: If you want to use some lib in react, try to search react-nameoflib. Most libraries have his react version.

Upvotes: 1

Related Questions