remixjs 添加mathjax 支持

网页端渲染公式是常见的需求,而其中mathjax的效果最好,因而想要在remixjs项目中添加mathjax支持.

mathjax官网给出的示例大多是在纯html文件下的方案,只需要在head标签栏引入mathjax脚本,随后页面的tex内容即可被正确解析和渲染. 然而在remixjs项目中,一般路由界面并没有head标签,如果直接在root.tsx的head标签中引入script,很多路由页面会格式错乱, 即全局引入script是很危险的事情. 因而只能考虑对指定路由来引入script.

网上给出的解决方案是使用useEffect函数.

import { useEffect, useState } from "react";
export default function Index() {
  const [inputValue, setInputValue] = useState("$$\\beta$$");
  useEffect(() => {
    const script = document.createElement("script");
    script.id = "mathjax-script";
    script.src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js";
    script.async = true;
    document.head.appendChild(script);
  }, []);
  return (
      <h3>React Hook MathJax</h3>
      <input
        type="text"
        defaultValue={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      {inputValue}

等价于在原始html中的head内部添加script,随后在jsx返回值里面直接书写latex即可正常渲染.

然而这样使用有个较为严重的问题, 如果渲染input内部的latex脚本, 这时候input value绑定状态量, 这个状态量只会在初始化时(即页面第一次加载时)渲染, 随后再更改input的值,不会再次触发渲染. 这种适用于从服务器读取数据,然后一次性渲染,然而如果有实时渲染需求的话,就无法满足了.

随后在npmjs上找了几种方案,但总是会提示 window not defined. 起初我以为是版本不兼容,可换了好几个包,都无法实现正常加载tex组件,上网一搜,原来是remixjs默认服务端渲染,而mathjaxjs依赖于浏览器对象. 因而需要使用ClientOnly组件来保证mathjax被客户端正常渲染.

最终选定了 react-hooks-mathjax库,

import { Input } from "antd";
import React from "react";
import Tex2SVG from "react-hook-mathjax";
import { ClientOnly } from "remix-utils";
type TexProps = {
  texStr: string;
const Tex: React.FC<TexProps> = (data) => {
  const { texStr } = data;
  return (
    <ClientOnly fallback={<div>Loading...</div>}>
      {() => <Tex2SVG class="tex" tabindex={-1} latex={texStr} />}
    </ClientOnly>
export default function Index() {
  const [inputValue, setInputValue] = React.useState("");
  return (
      <h3>React Hook MathJax</h3>
      <Input
        type="text"
        value={inputValue}
        onChange={(e) => {
          setInputValue(e.target.value);