How can I use KaTeX in React project? - javascript

How can KaTex be used in a React project? I read through documentation on NPM and still don't get it.
I don't quite see how katex.render and katex.renderToString can be applied to React.

katex.render needs a DOM element to render in, you can get one with a useRef hook.
const KaTeXComponent = ({texExpression}) => {
const containerRef = useRef();
useEffect(() => {
katex.render(texExpression, containerRef.current);
}, [texExpression]);
return <div ref={containerRef} />
}

Related

How to render a user defined react component?

In my electron application, I want to offer some kind of extensibility for the UI.
The users should be able to create a react component inside of a text editor inside of my application. Their code gets saved to a file. They also can define where the component should be placed inside my UI.
The problem is: I can't seem to figure out how to parse and inject the user-defined JSX in my existing react app.
I thought I try with babel transformSync, but I can't get it working. This is what I have so far:
const DynamicComponent = (props) => {
const reactCode = `
return (
<div>
<h1 >Hello, World!</h1>
</div>
)
`;
const transpiledCode = transform(reactCode, {
presets: ["#babel/preset-react"]
}).code;
console.log("transpiled", transpiledCode);
globalThis["React"] = React;
const Dynamic = new Function(`return ${transpiledCode}`)();
return <Dynamic />;
};
https://codesandbox.io/s/mobx-state-tree-todolist-3umd4?file=/components/TodoList.js
I'd like to have the "<h1>Hello, World!</h1>" rendered below my todo list.
This took sometime. Here are somethings to be aware of:
The #babel/core gives an invalid version error, so I used the #babel/runtime version since it's designed for use in the browser.
I found out that removing comments while using the #babel/core helped with automatic semicolon insertion problem with js.
You need to return your new DynamicComponent directly. Do not convert it to ReactElement by returning <Dynamic />.
See codesandbox here
const DynamicComponent = (props) => {
const reactCode = `
<div>
<h1>Hello, World!</h1>
</div>
`;
const transpiledCode = transform(reactCode, {
presets: [
"react",
{
comments: false
}
]
}).code;
globalThis["React"] = React;
return new Function(`return ${transpiledCode}`)();
};

Why does my React app not re-render or display changes on the DOM except I relaod?

import { useEffect, useState } from "react";
function Popular() {
const [popular, setPopular] = useState([]);
useEffect(() => {
getPopular();
}, []);
const getPopular = async () => {
const api = await fetch(
`https://api.spoonacular.com/recipes/random?apiKey=${process.env.REACT_APP_RECIPE_API_KEY}&number=9`
);
const data = await api.json();
setPopular(data.recipes);
};
return (
<div>
{popular.map((recipe) => {
return (
<div>
<p>{recipe.title}</p>
</div>
);
})}
</div>
);
}
export default Popular;
I am pretty new to React, and I encountered this issue which I have been trying to fix to no avail. The code is a component that is to return a list of recipe title to my app. I am fetching data from an API in the getPopular() function which is set to the setPopular function variable of the useState() method. But when I save my work and return to the browser, the changes does not display. The list does not display, but if I console.log(data.recipes) it displays on the console.
Before now, if I made any change (maybe a text change) the React app renders it without reloading, but now I have to reload the page before I see the change.
Please how do I fix this issue? So that I can see changes without having to reload the page manually.
Not saying that this is the problem, but getPopular() should not be called after its declaration? By this I mean:
const getPopular = async () => {
const api = await fetch(
/...
};
useEffect(() => {
getPopular();
}, []);
Another thing that bugs me is, although JS/React is case sensitive, I really think you should avoid having a const called popular, since your functions is Popular.
Please, let me know if the order did matter for your problem. I will review some react classes soon, if i get another inside, i'll let you know.

import npm modules asynchronously in react.js

I am using parcel as a bundler in React.js project.
How to load npm modules asynchronously in react.js?
There is only one page that uses one specific npm module so I didn't need to load it at first loading.
By avoiding this, I would like to reduce the bundle size.
Could you let me the proper way to do this?
========================
And also, if I understood anything wrongly about the bundle size optimization and lazy loading, please let me know.
By using Dynamic Import you may import the package when you really need the package.
You can use a dynamic import inside an useEffect hook like:
const Page = (props) => {
useEffect(
() => {
const [momentjsPromise, cancel] = makeCancelable(import("moment"));
momentjsPromise.then((momentjs) => {
// handle states here
});
return () => {
cancel?.();
};
},
[
/* don't forget the dependencies */
],
);
};
You can use dynamic imports.
Let's say you want to import my-module:
const Component = () => {
useEffect(() => {
import('my-module').then(mod => {
// my-module is ready
console.log(mod);
});
}, []);
return <div>my app</div>
}
Another way is to code-splitt Component itself:
// ./Component.js
import myModule from 'my-module';
export default () => <div>my app</div>
// ./App.js
const OtherComponent = React.lazy(() => import('./Component'));
const App = () => (
<Suspense>
<OtherComponent />
<Suspense>
);
my-module will be splitted along with Component.
These two patterns should work with any bundler, but it will work client side only.

How do I get the width of a div in a window after it got rendered by react?

I am trying to get the size of a div (css width: 100%) and set the zoom factor of a component according to the div's size. The problem is on first run of the App, the div doesn't exist in the DOM yet, since return has not created it. So I tried to put it in a useEffect to check if it gets created. However for some reason this doesn't work, when I put the querySelector into a constant because I thought the constant would be recreated once the component renders again because useEffect should trigger a re-render. When I put it directly into the deps, I get a warning about exhaustive deps.
React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps
How do I properly extract the querySelector into a variable to have the app react to it being rendered?
const App = () => {
const querySelector = document.querySelector(".area-main"); //doesn't exist on 1st render but should be recreated on second render?
const [zoom, setZoom] = useState(1);
useEffect(() => {
if(querySelector && querySelector.clientWidth > 1000){
setZoom(1.5);
}
console.log(querySelector); // null
}, [querySelector]); //chould change on re-render from null to element but doesn't trigger
// the following works but complains about complex dependency tells me to extract (how?)
//}, [document.querySelector(".area-main")]);
useEffect(() => {
console.log(zoom); //prints out 1 once
}, [zoom]);
return(
<div className=".area-main">
<Component zoom={zoom} />
</div>
);
}
According to the comment, I tried using layout effect but that doesn't change
const querySelector = document.querySelector(".canvas-main");
React.useLayoutEffect(() => {
console.log(querySelector); //prints out null once
}, [querySelector]);
To get a reference to the div element, you'll want to use a react ref. Then you can access that ref in a useLayoutEffect and do what you need to do. useLayoutEffect is similar to useEffect, except if you set state it will cause a synchronous rerender, and that way you won't have a flicker on the screen as you reposition things.
const App () => {
const [zoom, setZoom] = useState(1);
const divRef = useRef();
useLayoutEffect(() => {
if (divRef.current.clientWidth > 1000) {
setZoom(1.5)
}
}, []);
return (
<div ref={divRef} className=".area-main">
<Component zoom={zoom} />
</div>
);
}
For more information on refs, see this page

React js - div onClick link to page

I'm trying to simulate a link in react js clicking on a div.
This is my code:
function handleClick(myLink){
window.location.href=myLink;
}
and here where I call it:
<Col className="aslink" onClick={handleClick('/path/to/myUrl')}>
<div>...</div>
</Col>
But it goes directly to the URL without clicking, so it starts an infinite loop.
How can I solve it?
Many thanks in advance!
This is because you are calling the function in this part <Col className="aslink" onClick={handleClick('/path/to/myUrl')}> instead of providing reference to it to be used on users click action. What you can do is define it like this:
const handleClick = (myLink) => () => {
window.location.href=myLink;
}
then it will work as you want it.
handclick('your path')
is already running the code. Try
onClick = {() => handlick('your path')}
This will stop it from automatically running
First off, I would recommend using React Router's Link over history.location.href, as it uses the routers history api, using this declarative, accessible navigation instead of history means that you are safe to any changes to the history api in the future.
You can import it like this:
import { Link } from 'react-router-dom'
Secondly, you were calling the handleClick function instead of executing the function.
If you use react-router*(which is most possible - if not - then you should research value of this)* then you can get access to browser-history via react router provider
pass router api to your component
if you use modern react version - use hook useHistory -
const Comp = () => {
const history = useHistory()
const handleRedirect = useCallback((path) => {
return () => {
history.push(path);
}
}, [])
return <div onClick={handleRedirect('path-to-page')}>Navigate</div>
}
export default Comp;
or 2. extract history object from taken props in your component
you can wrap you component by HOC - withRouter.
const Comp = ({history}) => {
const handleRedirect = useCallback((path) => {
return () => {
history.push(path);
}
}, [])
return <div onClick={handleRedirect('path-to-page')}>Navigate</div>
}
export default withRouter(Comp)

Categories