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.
Related
I'm working on a large JS component library (50+ components) and trying to introduce TS support so that components can be written in Typescript OR Javascript, and imported from either TS or JS projects. Because of the large number of components, I'm not rewriting every component - just a single component to start with.
The compilation and export of components works fine for TSX and simple JSX components, but for even slightly more complex JSX, it breaks down.
Here's a simple JSX component that I'm not ready to convert to TSX yet:
import { string } from 'prop-types';
import React from 'react';
export default function MyComponent(props) {
const { className, ...otherProps } = props;
const fullClassName = [className, 'my-component-class'].join(' ');
return <div className={fullClassName} {...otherProps} />;
}
MyComponent.propTypes = {
className: string,
};
...and the corresponding generated .d.ts file:
declare function MyComponent(props: any): JSX.Element;
declare namespace MyComponent {
namespace propTypes {
export { string as className };
}
}
export default MyComponent;
import { string } from "prop-types";
In theory, now, I should be able to import and use this in a TS project:
import { MyComponent } from 'some-common-lib';
export default function SomeWidget() {
return (
<MyComponent>
<div>some text</div>
<div>some other text</div>
</MyComponent>
);
}
...but this generates a type error, because I'm passing an implicit children prop which isn't recognised:
Type '{ children: Element[] }' is not assignable to type 'IntrinsicAttributes & InferPropsInner<Pick<typeof propTypes, never>> & Partial<InferPropsInner<Pick<typeof propTypes, "className">>>'.
Property 'children' does not exist on type 'IntrinsicAttributes & InferPropsInner<Pick<typeof propTypes, never>> & Partial<InferPropsInner<Pick<typeof propTypes, "className">>>'.
My assumption is that this long constructed type is being used internally by React as part of React.createElement(), but the documentation for Typescript doesn't cover the process.
While I could solve this problem by rewriting every single component in the library to Typescript, we're not ready to do that yet as it's too much of a commitment/risk (and too much testing to go through for each application that uses the components).
I had the idea of writing a script that deletes type definition files for all JS-only components, and only leaves them in place for TS components (and index.js files that export them). This seems like an inelegant kludge but may be my only option.
My hope was that there is a way to either:
configure the output of tsc so that proptypes for JS components are more permissive (the documentation is not clear on this!) OR
configure the output of tsc so that it doesn't produce type declarations for JS component files OR
configure the compilation of consuming applications so that it ignores inferred prop types and instead just treats the components as plain functions that allow any props (as it would if we were importing a plain JS library). Again, TS documentation doesn't acknowledge this behaviour, so I have no idea how this would work.
I can't see a way to accomplish any of these - are they possible?
Since React 18 and the corresponding version of #types/react, you need to explicitly specify the children prop.
import { string, element } from 'prop-types';
import React from 'react';
export default function MyComponent(props) {
const { className, ...otherProps } = props;
const fullClassName = [className, 'my-component-class'].join(' ');
return <div className={fullClassName} {...otherProps} />;
}
MyComponent.propTypes = {
className: string,
children: element,
};
I'm not used to using prop-types so you might need to tweak this code.
If you were only targeting Typescript, you could rewrite your code this way:
import React from 'react';
interface MyComponentProps {
className: string;
children?: ReactNode;
}
export default function MyComponent({ className, ...otherProps }: MyComponentProps) {
const fullClassName = [className, 'my-component-class'].join(' ');
return <div className={fullClassName} {...otherProps} />;
}
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) => {}
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?
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>
)
}
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