How can I Parse a string containing JSX into JSX components - reactjs

I am currently trying to parse a string that contains JSX into literal JSX,and inject it into the return of my component:
import react from "react";
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(theme => ({
heading:{
color: theme.palette.secondary.dark
},
});
const Foo = () => {
const classes = useStyles();
const jsxString = "<span>foo <span className={classes.heading}>bar</span></span>"
// convert or parse jsxString into literal JSX that can be rendered or returned
// by the component.
const jsxReact = ConvertToWhatReactUnderStands(jsxString)
return (
<>
{jsxReact}
</>
);
};
export default Foo;
I am using create-react-app, and I am not intending on ejecting.
I have tried the following:
dangerouslySetInnerHTML and this does not work, when inspecting the the element I get:
<span>foo <span className="{classes.heading}">bar</span></span>
How do I go about achieving this, making sure that my styles are applied?
Also, the inner html <span/> tag was a Material-UI <Typography/> component, I had to change it because parsing it changed the component name to : <typography/> after using the following functions from these packages:
import parse from 'html-react-parser';
import ReactHtmlParser from 'react-html-parser';
import Parser from 'html-react-parser'
and the following construct dangerouslySetInnerHTML
I understand that , I would have to transpile/transform the JSX string into javascript code with something like Babel before I execute it.
For example, when using the browser version of Babel:
var jsCode = babel.transform(jsxString);
eval(jsCode.code);
But ejecting and using Babel is not an option for me.
To be breif, my question is how would I convert a string into JSX and make sure that my style Classes are are applied? Is it possible without using babel?

Related

I overridden React.createElement, but jsx's button doesn't seem to call React.createElement

I overridden React.createElement, but jsx's button doesn't seem to call React.createElement.
React.createElement did not get the value of jsx as button.
The following is excerpted from: https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
React 17 introduces two new entry points to the React package that are intended to only be used by compilers like Babel and TypeScript. Instead of transforming JSX to React.createElement, the new JSX transform automatically imports special functions from those new entry points in the React package and calls them.
function App() {
return <h1>Hello World</h1>;
}
This is what the new JSX transform compiles it to:
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
So, you should rewrite jsx function.
// Prod mode
import jsxRuntime from 'react/jsx-runtime'
const jsx = jsxRuntime.jsx
jsxRuntime.jsx = (...args) => {}
// Dev mode
import jsxRuntime from 'react/jsx-dev-runtime'
const jsx = jsxRuntime.jsxDEV
jsxRuntime.jsxDEV = (...args) => {}

React Monaco-Editor typescript Integration

According to the docs of #monaco-editor/react we need the original monaco-editor as a peer dependency. I installed that as a dev dependency and tried to add node_modules/monaco-editor/monaco.d.ts to the include block in my tsconfig.json. But I am still not able to access the type declarations after this. the only way I could get it to work was to import it like this.
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
but now my type declarations are throwing error when I try to define them for editor and monaco variables. Does anybody know hot get these working ?
here's my code when trying to define the types.
import { useState, useRef } from 'react';
import Editor from '#monaco-editor/react';
import styles from '../Code.module.css';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
type Language = {
languageName: string;
languageID: number;
};
type EditorProps = {
language: Language;
onCodeChange: Function;
};
type Monaco = typeof monaco // to use it as a type for the monaco variable in handleEditorDidMount function
const CodeEditor = ({ language, onCodeChange }: EditorProps) => {
console.log('language is ', language);
const [editorValue, setEditorValue] = useState('//some comment');
const editorRef = useRef(null);
const monacoRef = useRef(null);
const handleEditorDidMount = (editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => {
editorRef.current = editor;
monacoRef.current = monaco;
console.log(monaco.languages.getLanguages());
console.log('editor', editor.getModel().getLanguageIdentifier().language);
};
const handleEditorValueChange = (value: string|undefined) => {
//console.log('event is ', event);
setEditorValue(value);
};
const editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
fontSize: 15,
minimap: {
enabled: true,
},
};
return (
<div className={styles.editorBlock}>
<div className={styles.editorNav}>
<button>Submit</button>
<button>Change Language</button>
</div>
<Editor
height="90vh"
language={language.languageName}
defaultValue={editorValue}
onMount={handleEditorDidMount}
onChange={handleEditorValueChange}
options={editorOptions}
/>
</div>
);
};
In my handleEditorDidMount function, the argument monaco throws the error, cant use import as a type
You make a number of wrong assumptions in your code.
Importing a typings file like a normal module won't make the typings available in your IDE.
Monaco has named exports, so use something like: import { languages, editor } from "monaco-editor/esm/vs/editor/editor.api";. This imports the two namespaces languages and editor.
The module react-monaco exports MonacoEditor, not Editor.
monaco (as you imported it) is just the exports object from the monaco module. What do you expect form "typeof monaco"? Makes no sense.
What is the reference to monaco supposed to contain? A reference in React holds a pointer to an object, something that either was instantiated in the DOM or is the instance of a React component. Make no sense to have that ref for the monaco export.
A ref is usually initialised automatically in the render call, like:
<MonacoEditor
ref={this.editorRef}
language={language}
options={opts}
/>
so you don't need the mount function to set that.

install quilljs-markdown on react

I need such an editor on react https://cloverhearts.github.io/quilljs-markdown/ , as you can see in it you can put markdown characters directly into the text.
when I do this
import React, { Component } from 'react'
import './App.css'
import ReactQuill from 'react-quill'
import Quill from 'quill'
import QuillMarkdown from 'quilljs-markdown'
const App = () => {
const editor = new Quill('#editor', {
theme: 'snow'
})
new QuillMarkdown(editor)
return (
<div className='app'>
{/*<MyComponent/>*/}
<div id="editor"></div>
</div>
)
}
export default App
I get error TypeError: Cannot read property 'on' of undefined
as I understand I need jQuery for work, but I use react, I found https://www.npmjs.com/package/react-quill this quilljs for react, but I don't know how to combine it with markdown https://www.npmjs.com/package/quilljs-markdown
can anyone help?
I found the solution for this after hours of trying this out.
What you have to do is this:
Create a module for ReactQuill
Register the module.
Pass modules to react quill
Shown Below.
Step 01
const modules = {
markdownOptions: {}
};
Step 02
Quill.register('modules/markdownOptions', QuillMarkdown);
Step 03
<ReactQuill
modules={modules}
/>
It seems like you are trying to initialize the Quill instance and the markdown module before the editor is ready.
Use useEffect hook to initialize it after the div has been rendered:
import {useEffect} from 'react';
...
useEffect(() => {
const editor = new Quill('#editor', {
theme: 'snow'
});
new QuillMarkdown(editor);
});

How do I use getElementsByClassName when I'm using CSS modules in React.js?

I want to apply getElementsByClassName to a element with dynamic name assigned by CSS modules in React.js. For example, if I named a class as 'firstLink' using className={styles.firstLink} in a file named RegisterPage.js, the resulting name for the class is:
The __1Ozd bit is random. How can I apply getElementsByClassName in this situation ?
CSS modules provides key-value object which you can use in the code.
Key is class name defined by you, value is generated class name.
import React from 'react'
import style from './style.module.css'
export default function Component() {
React.useEffect(() => {
console.log(document.getElementsByClassName(style.firstLink))
}, [])
return <div className={style.firstLink} />
}
And I believe there can't be any reason to use vanilla js functions like getElementsByClassName, using state and in some cases using refs should cover all cases, example:
import React from 'react'
export default function Component() {
const ref = React.useRef()
React.useEffect(() => {
console.log(ref.current) // Will output dom element
}, [])
return <div ref={ref} />
}
After some thinking, perhaps there are old-school libraries which can accept root element only by class name.
In react, behaviors such as you wanted is termed Refs, see this.
If you are building functional component do this:
const {useRef} = React
const Component =>{
styelRef = useRef()
//refer to your anchor tag style here which should be called className
const styleFnc=()=>{
styleRef.current.className = "__1Ozd"
}
return(
<a ref={styleRef} href="...." className={`RegisterPage_firstLink${styelFnc}`}></a>
)
}

Use SVGR and Material UI together

i'm trying to use SVGR to convert my svg into react components and i need to use <SvgIcon /> of Material UI and pass the converted component as a prop to it.
nothing wrong with this yet.
but,
SVGR saves these component in a folder called svgCom for example and inside of this folder there is index.js plus converted svg components.
i need to wrapp all these components inside of <SvgIcon> so i don't have to wrap this icon with <SvgIcon/> again for each use case;
so far i try to add this component in template.js of SVGR but it throw me an error when try to parse .
is this the best way of doing such thing or there is better way ?
if it is what's wrong with my template.js ?
here is my templete.js :
function template(
{ template },
opts,
{ imports, componentName, props, jsx, exports },
) {
return template.ast`
${imports}
import { SvgIcon } from "#material-ui/core";
///////////////////////////////////// error here
const ${componentName} = (${props}) => <SvgIcon component={${jsx}} />
${exports}
`
}
module.exports = template
thank you.
We had the same problem and after some research I came to the following solution:
const {
identifier,
jsxClosingElement,
jsxElement,
jsxIdentifier,
jsxOpeningElement,
jsxSpreadAttribute,
} = require('#babel/types')
const iconTemplate = ({ template }, _, { componentName, jsx, exports }) => {
const wrappedJsx = jsxElement(
jsxOpeningElement(jsxIdentifier('SvgIcon'), [jsxSpreadAttribute(identifier('props'))]),
jsxClosingElement(jsxIdentifier('SvgIcon')),
[jsx],
false
)
return template.ast`
import React from 'react'
import SvgIcon from '#material-ui/core/SvgIcon'
const ${componentName} = (props) => ${wrappedJsx}
${exports}
`
}
module.exports = iconTemplate
If you don't want {...props} on the <svg> tag, you have to disable the expandProps option

Resources