Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/407.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 惯用的重DOM操作反应(MathJax)_Javascript_Reactjs_Typescript_Mathjax - Fatal编程技术网

Javascript 惯用的重DOM操作反应(MathJax)

Javascript 惯用的重DOM操作反应(MathJax),javascript,reactjs,typescript,mathjax,Javascript,Reactjs,Typescript,Mathjax,我正在React应用程序中使用MathJax。MathJax带来了很多复杂性:它有自己的并发管理系统,并对React不知道的DOM进行更改。这导致了大量DOM微观管理,在React中通常被认为是反模式的,我想知道我的代码是否可以做得更好 在下面的代码中,MJX是一个组件,它接受一个TeX字符串作为输入,并将其馈送到MathJax中RenderGroup是一个方便的组件,它可以跟踪其所有MJX子体何时完成排版 // 从“React”导入*作为React; /*一旦MathJax加载并准备就绪,承诺

我正在React应用程序中使用MathJax。MathJax带来了很多复杂性:它有自己的并发管理系统,并对React不知道的DOM进行更改。这导致了大量DOM微观管理,在React中通常被认为是反模式的,我想知道我的代码是否可以做得更好

在下面的代码中,
MJX
是一个组件,它接受一个TeX字符串作为输入,并将其馈送到MathJax中
RenderGroup
是一个方便的组件,它可以跟踪其所有
MJX
子体何时完成排版

//
从“React”导入*作为React;
/*一旦MathJax加载并准备就绪,承诺就会解决*/
export const MathJaxReady=新承诺((解析、拒绝)=>{
常量脚本=$(“#js async mathjax”);
如果(!script)返回;
if(window.hasOwnProperty(“MathJax”)){
MathJax.Hub.Register.StartupHook(“结束”,解析);
}否则{
script.addEventListener(“load”,()=>MathJax.Hub.Register.StartupHook(“End”,resolve));
}
});
界面道具扩展React.HTMLAttributes{
显示?:布尔值;
}
导出类MJX扩展了React.Component{
私有resolveReady:()=>void;
DOMELENT:HTMLSPANLENT;
jax:MathJax.ElementJax;
//最初排版后解决的承诺
准备:承诺;
静态defaultProps={
显示:假
}
建造师(道具:道具){
超级(道具);
this.ready=新承诺((解决,拒绝)=>this.resolveReady=解决);
this.Typeset=this.Typeset.bind(this);
}
异步组件didmount(){
等待你做好准备;
这个.排版()
.then(()=>this.jax=MathJax.Hub.getAllJax(this.doElement)[0])
。然后(这个。准备就绪);
}
shouldComponentUpdate(下一步,下一步状态){
/*原来的span已经被MathJax吃掉了,我们自己管理更新*/
const text=this.props.children数组实例?this.props.children.join(“”):this.props.children,
nextText=nextProps.children数组实例?nextProps.children.join(“”):nextProps.children;
//重播机?
if(this.jax&&text!==nextText){
this.jax.Text(nextrops.children);
}
//课程改变了?
if(this.props.className!==nextrops.className){
const classes=this.props.className?this.props.className.split(“”):[],
newClasses=nextrops.className?nextrops.className.split(“”:[];
const add=newClasses.filter(=>!classes.includes()),
remove=classes.filter(=>!newClasses.includes());
用于(移除常数)
this.domeElement.classList.remove(_);
用于(添加常数)
this.domeElement.classList.add(_);
}
//样式属性已更改?
if(JSON.stringify(this.props.style)!==JSON.stringify(nextrops.style)){
Object.keys(this.props.style | |{})
.filter(=>!(nextrops.style | |{}).hasOwnProperty(290;))
.forEach(=>this.props.style[]=null);
Object.assign(this.domeElement.style,nextProps.style);
}
返回false;
}
Typeset():承诺{
返回新承诺((解决、拒绝)=>{
Queue([“Typeset”,MathJax.Hub,this.domeElement]);
队列(解析);
});
}
render(){
const{children,display,…attrs}=this.props;
常量[打开,关闭]=显示?[“\\[”,“\\]”]:[“\\(“,“\\)”];
返回(
this.domeElement=node}>{open+children+close}
);
}
}
//等待一大堆东西被呈现出来
导出类RenderGroup扩展React.Component{
私人承诺:承诺[];
准备:承诺;
componentDidMount(){
this.ready=Promise.all(this.promises).then(()=>{});
}
render(){
this.promises=[];
返回递归映射(this.props.children,node=>{
if(typeof node.type==“函数”&&node.type.prototype实例MJX){
const originalRef=node.ref;
返回React.cloneElement(节点{
参考:(参考:MJX)=>{
如果(!ref)返回;
这个.promises.push(ref.ready);
if(原始参考类型==“函数”){
原始参考(ref);
}else if(originalRef&&typeof originalRef==“对象”){
原始参考电流=参考;
}
}
});
}
返回节点;
});
}
}
//递归React.Children.map
导出函数递归映射(
子项:React.React节点,
fn:(child:React.ReactElement;根据React团队的说法,这是个问题。问题是函数组件没有实例,所以我不知道如何将
.ready
公开给父组件,比如
Example
。我知道这个场景有一个钩子,但这似乎取决于最终对HTML组件的引用uess在
MJX
的情况下,我可以在
上放置一个ref,但这对
渲染组
不起作用

  • 强制管理输入是一件痛苦且容易出错的事情。有没有办法恢复React的声明性优点

  • 另外:我还没有弄清楚如何正确地键入
    recursiveMap
    ;TypeScript对
    fn(child)
    行很生气。用泛型替换any也不错


  • 我个人没有使用MathJax,但根据我的经验,处理
    resolveReady
    内容的“惯用反应”方式可能是通过上下文传递回调,让孩子们在加载或准备好时通知家长。例如(使用挂钩!):

    const LoadingContext=createContext(()=>()=>{});
    const LoadingProvider=memo(LoadingContext.Provider);
    函数RenderGroup({children}){
    常量[areChildrenReady,setAreChildrenReady]=使用状态(false);
    const nextchildiref=useRef(0);
    const unfinishedChildrenRef=useRef(new Set());
    const startLoading=useCallback(()=>{
    const childId=nextCh
    
    const LoadingContext = createContext(() => () => {});
    const LoadingProvider = memo(LoadingContext.Provider);
    
    function RenderGroup({ children }) {
      const [areChildrenReady, setAreChildrenReady] = useState(false);
    
      const nextChildIdRef = useRef(0);
      const unfinishedChildrenRef = useRef(new Set());
      const startLoading = useCallback(() => {
        const childId = nextChildIdRef.current++;
        unfinishedChildrenRef.current.add(childId);
        setAreChildrenReady(!!unfinishedChildrenRef.current.size);
        const finishLoading = () => {
          unfinishedChildrenRef.current.delete(childId);
          setAreChildrenReady(!!unfinishedChildrenRef.current.size);
        };
        return finishLoading;
      }, []);
    
      useEffect(() => {
        if (areChildrenReady) {
          // do whatever
        }
      }, [areChildrenReady]);
    
      return (
        <LoadingProvider value={startLoading}>
          {children}
        </LoadingProvider>
      );
    }
    
    function ChildComponent() {
      const startLoading = useContext(LoadingContext);
      useEffect(() => {
        const finishLoading = startLoading();
        MathJaxReady
          .then(anotherPromise)
          .then(finishLoading);
      }, [startLoading]);
      return (
        // elements
      );
    }