How to Use SVG with React and ReasonML? - reactjs

With create-react-app and JavaScript/TypeScript, I understand I'm able to "import" an SVG as noted below. How may I do so with ReasonML?
import { ReactComponent as Logo } from './logo.svg';
function App() {
return (
<div>
{/* Logo is an actual React component */}
<Logo />
</div>
);
}

Create React App uses webpack to transform SVG files into React components. If you’re using Reason with CRA, then all you need to do is provide a binding to the generated component. However, CRA will only transform the SVG into a component if the import statement is written exactly a certain way, which isn't how BuckleScript outputs import statements. (There's a GitHub issue about it here.) You have to import it with raw JavaScript and then bind to the imported value:
%bs.raw
{|import {ReactComponent as _Logo} from "./logo.svg"|};
module Logo = {
[#react.component] [#bs.val] external make: unit => React.element = "_Logo";
};
/* And we can use it like a regular component: */
[#react.component]
let make = () =>
<div>
<Logo />
</div>;
According to the CRA docs:
The imported SVG React Component accepts a title prop along with other props that a svg element accepts.
For any of the other props you want to use, you'll have to add them to your external binding.
If you're not using CRA, then you'll need to configure your bundler to do the same transformation. I'm not familiar with the internals of CRA, but this seems to be the relevant code from its webpack configuration.

We can use SVGR to handle the webpack loading and then import the module as we normally would.
const webpack = require('webpack');
module.exports = {
entry: './src/index.js',
module: {
rules: [
//...
{
test: /\.svg$/,
use: ['#svgr/webpack'],
},
],
},
//...
};
module Logo = {
#bs.module(`../../../../src/img/logo.svg`) #react.component
external make: {.} => React.element = "default"
}
...
<Logo /> // works
source: https://blog.logrocket.com/how-to-use-svgs-in-react/

I am the author of another solution that doesn't involve webpack.
It's a tool that can transform your svg files directly into .re files: https://github.com/MoOx/react-from-svg
This can create files for react (dom) or react-native(-web) (=> files generated use react-native-svg).
Feel free to try it :)
For example you can do (when the tool is installed from npm)
$ react-from-svg src/SVGs src/SVGs/components --with-native-for-reason --remove-fill
This will turns the files from svg/SVGs into React components into src/SVGs/components compatible for React Native with the Reason syntax.
The last option remove all svg fill props so you can use them as icons.
Note that the generated components accept width, height & fill props so you can adjust them when used.
Last bonus: since webpack is not involved, you can use this transformation only when you update your SVGs files & use this code directly with a Node runtime (JSX from Reason gets removed when converted to JS so the code can be consumed directly via Node without any transformation - which can be handy for tiny static sites/pages).

Related

Dynamically changing Less variables in React Gatsby app with Ant Design at runtime

We are building a White Label platform using React, GatsbyJs and Ant Design. We are stuck with Gatsby and Ant Design because we are migrating from an existing system and changing any of those would bring huge impact. Also, we must have a single deploy. Having a build for each White Label is not an option.
So, we need to be able to change style (mainly color) at runtime.
The problem is: Ant Design uses less variables to define it's themes and we're not able to change them at runtime, not even with less's modifyVars.
The thing is we MUST change less variables, and not global CSS or use other means
Ant Design derivates the main variables many times to get adjacent properties. So, for instance, if we define #primary-color as red, when we add a Button to the screen, Ant Design also defines it's border color, hover color, and many other details with different shades of red.
This means that, if we were to use other styling tool, we would need to generate those color derivations and replace every little property for every component. This would be chaos.
Scenario
We are using gatsby-plugin-antd and gatsby-plugin-less to load less and change vars at build time. Our gatsby-config.js looks like this:
module.exports = {
siteMetadata: {
siteUrl: 'https://www.yourdomain.tld',
title: 'yourtitle'
},
plugins: [
'gatsby-plugin-root-import',
'gatsby-plugin-typescript',
{
resolve: 'gatsby-plugin-antd',
options: {
style: true
}
},
{
resolve: 'gatsby-plugin-less',
options: {
lessOptions: {
javascriptEnabled: true,
modifyVars: {
'primary-color': '#FFFFFF',
'link-color': '#000000',
'success-color': '#FFFFFF',
'warning-color': '#000000'
}
}
}
}
]
};
We import styling in our gatsby-browser.js file:
import './src/styles/index';
Our styles/index has:
import 'tachyons';
import './global.css';
import './antd.less';
antd.less:
#import '~antd/dist/antd.less';
And global.css has some general CSS for the project.
It's working fine with the defined variables at build time.
What we attempted so far...
We have tried out this plugin:
https://github.com/mzohaibqc/antd-theme-webpack-plugin
Which supposedly does exactly what we need. But there's no example using Gatsby.
We then tried to add the plugin using the gatsby-node.js as mentioned here:
https://www.gatsbyjs.com/docs/how-to/custom-configuration/add-custom-webpack-config/
First, we tried using index.html as the indexFileName for the pluggin. It just doesn't work.
Then, following the plugin docs, we tried using indexFileName as false and importing the following scripts using Helmet at our pages/index.tsx:
<script> window.less = { async: false, env: 'production' };
</script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.2/less.min.js"></script> ```
Also didn't work. If we define indexFileName as false, we get memory over the heap.
If we keep indexFileName as 'index.html' and just add the scripts, we are able to call window.less.modifyVars and it returns successfully (we are logging the Promise's then and error) but it doesn't affect antd's variables.
We then tried doing something similar, but instead of loading less externally, we installed it as a node_module and imported it to the file and used it directly in a similar fashion. Got the same result: modifyVars runs and returns successfully but doesn't affect antd.
Then, we tried something a bit different: we removed gatsby plugins and tried importing less from antd directly, as suggested here:
https://ant.design/docs/react/customize-theme
So we imported it like this:
#import '~antd/lib/style/themes/default.less';
#import '~antd/dist/antd.less';
#import 'your-theme-file.less';
Also, no good. It's different from the previous scenario, because style gets updated after you save your code. No need to stop Gatsby, as the first solutions. But, modifyVars still has no affect on antd components.
Then, to isolate the issue, we tried to style a basic HTML component - a button - to check if the issue was with gatsby or antd. And... still no success. less.modifyVars didn't work to change a basic button style on runtime.
So, we think it's probably something between Gatsby and Less. We checked gatsby-plugin-antd and gatsby-plugin-less to see if we could find something, but found nothing useful.
We assume that the "less instance" or "less context" used by gatsby's less-loader during build time is not the same we are calling modifyVars on. So it doesn't affect the original vars.
Totally stuck. Please, help!
EDIT - SOLUTION
Ant Design team has just released - TODAY - a new alpha version that includes dynamic theming, using CSS Variables.
https://ant.design/docs/react/customize-theme-variable
It works fine, so far. Closing the issue.
EDIT 2
There's a more detailed solution on the accepted answer.
Ant Design team has just released - TODAY - a new alpha version that includes dynamic theming, using CSS Variables.
https://ant.design/docs/react/customize-theme-variable
It works fine, so far.
EDIT - Detailed solution
I removed gatsby-plugin-antd and gatsby-plugin-less from the project. Also removed the import of antd less file.
Instead, in my styles/index.tsx (which is imported in gatsby-browser.js), I'm importing the variables.min.css file:
import 'antd/dist/antd.variable.min.css';
Then, whenever I want to change Ant Design variables, I just use:
import { ConfigProvider } from 'antd';
...
ConfigProvider.config({
theme: {
primaryColor: '#[DESIRED_COLOR_HEX]'
}
});
Provider
Since this has to be done every time the site is loaded, I'm creating a ThemeProvider that wraps every page and defines the theme. It fetches theme data from the backend and sets Ant Design theme variables.
Example code:
import { Spin } from 'antd';
import React, { useEffect, useState } from 'react';
import { ConfigProvider } from 'antd';
import { Theme } from './theme.interface';
interface Props {
children: React.ReactNode;
}
export const ThemeProvider = ({ children }: Props): JSX.Element => {
const [themeVars, setThemeVars] = useState<Theme>(null);
useEffect(() => {
async function fetchMyAPI() {
const result = await getThemeFromBackend(); // Make API call with Axios
if (result) setThemeVars(result);
}
fetchMyAPI();
}, []);
useEffect(() => {
if (themeVars) {
ConfigProvider.config({
theme: {
primaryColor: `#${themeVars.primaryColor}`
}
});
}
}, [themeVars]);
return <div>{!themeVars ? <Spin size="large" /> : <>{children}</>}</div>;
};
And it can be used like this:
...
<ThemeProvider>
<h1>My page header</h1>
<p>Page content...</p>
</ThemeProvider>
...
Note: You can save theme data on local storage for performance improvement, so you don't have to call your backend everytime your site reloads. Maybe you'll just have to refresh it from time to time.

Can Someone help me to convert typescript code to React Code

There is a package called react-data-grid. And there is an example table on that particular package but the code is in TypeScript and also docs for this particular version are not there. I tried to convert the typescript to React. Everything was fine until I wanted to implement Drag and Drop. Some error is coming and I think that error is because I'm importing something in the wrong way. Can you tell me where I'm doing anything wrong?
Here is the Sandbox link.
The error in my local build is coming on RowRender.js in line number 44.
I also included the typescript file there.
You can also see the error if you just uncomment the line 72-74 of App.js component in sandbox.
Rename .ts file to .tsx file. In another case TypeScript will not understand <div> <Row> and other react elements
Update your imports regarding react-data-grid to: import { Row, RowRendererProps } from "react-data-grid";
Update your import import { useCombinedRefs } from ... to fetch hook from correct place.
If you will see error regarding default import for React - change import React from 'react' to import * as React from "react"; or update tsconfig to support synthetic imports
useCombinedRefs - is internal function that is not exported, so you can't use it directly. Option #1 - write it yourself. Option #2 find the reason why you are trying to use internal function. Should be the better way.
function useCombinedRefs(...refs) {
return useCallback(handle => {
for (const ref of refs) {
if (typeof ref === 'function') {
ref(handle);
} else if (ref !== null) {
ref.current = handle;
}
}
}, refs);
}

How to import bootsrap in just one component in react.js

Actually im "migrating" a website project where i used a template. There are some conflicts when i put the bootstrap link in the index.html. I would like to apply bootstrap just into one component to avoid this conflicts, but im not sure how to do it. Im pretty new with react.
The "conflicts" are just visual, like if importing bootstrap changes the rows and columns numbers
Unfortunately CSS is always global, so there's no easy way of doing this.
One way however, is to recompile Bootstrap and wrap it in a wrapper class.
Then, in your code, setup the wrapper class on a wrapper component and only classes that will be inside that wrapper component will be affected by Bootstrap classes.
Steps to do it :
(you'll need npm to do it)
download bootstrap sources here
unzip it, go in ./scss/bootstrap.scss
add a wrapper css class on all #import like so :
.local-bootstrap {
#import "function";
#import "variables";
/* ... */
#import "print";
}
go back to the root of the unzipped directory
run npm install and npm run css-compile
your local bootstrap is in ./dist/css/bootstrap.css, that's what you can add to your project
Then in your code :
<div class="local-bootstrap"> /* wrapper component */
/* inside, the code is affected by your local bootstrap */
<div class="alert alert-primary" role="alert"/>
</div>
/* outside it is not */
<div>
</div>
That said, it's pretty sure that the javascript part of bootstrap won't fully work because it relies on classes, this is a bit hacky, anyway.
If you're using SCSS, add the following to your SCSS file:
.local-bootstrap {
#import "~bootstrap/scss/bootstrap";
}
In your component file, make sure the SCSS file has been imported and then wrap the code you want to use bootstrap in a local-bootstrap classed div (see example).
Example:
import React from 'react';
import '<PATH TO SCSS FILE>';
const Example = () => {
return (
<div className='local-bootstrap'>
CODE YOU WANT TO USE BOOTSTRAP
</div>
);
};
You can use reactstrap, import what you need into your component file and leave the others alone.
Example:
import React from 'react';
import { Button } from 'reactstrap';
export default (props) => {
return (
<Button color="danger">Danger!</Button>
);
};
Using:
import 'bootstrap/dist/css/bootstrap.min.css';
You can import Bootstraps CSS into that specific component if you do not want it in your app.js or sitewide.

How to load css provided by webpack inside an IFrame created using "react-frame-component" library?

I'm trying to render my React component inside an iframe. I got it to work by using the library "react-frame-component". The problem is that the styles are loaded outside the iframe, at the end of "head" element. I want to change it to be loaded inside the "head" of "iframe" element.
I'm using Webpack to generate the bundles with JS and CSS included and I saw that I can change where "style-loader" will load the styles by setting the option "insertInto", but this is throwing an error:
Uncaught Error: Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.
This is my React component:
import Frame from 'react-frame-component'
...
ReactDOM.render(
<Frame id="someId">
<Provider store={Store.get()}>
<Container/>
</Provider>,
</Frame>,
document.body
);
This is my "style-loader" in Webpack configuration:
{
loader: 'css-loader',
options: {
insertInto: () => document.querySelector("#someId"),
},
}
I think the problem is that the component was not rendered when webpack tried to include the styles. How to solve this?
You would need to load the styles into a temporary element as placeholder, and then use Javascript to clone those styles into the new frame.
// index.html
...
<div id="dynamic-styles"></div>
...
// webpack.config.js
...
{
loader: 'css-loader',
options: {
insertInto: () => document.getElementById("dynamic-styles"),
},
}
...
// some.js
...
const stylesContainer = document.getElementById('dynamic-styles');
const frame = document.getElementById('someID');
const frameHead = frame.contentWindow.document.head;
[...stylesContainer.children].forEach(styleNode => {
const newStyle = document.createElement('style');
newStyle.textContent = styleNode.textContent;
frameHead.appendChild(newStyle);
});
I haven't tested the above snippet but I know the concept works. I feel it gets the point across.
There may be a better way to do this but I haven't looked into it enough to figure it out yet. It'd be nice to be able to get webpack to make separate style bundles so that each configured frame could just automatically lazy load it's own dedicated bundled css

Rollup and Post CSS - Auto-prefixing React className attributes

I'm using Rollup + React + Post CSS to build a component library. I'm looking for a way to autoprefix class names so that they will not conflict with styles in the project using the library.
I have already added this plugin to automate adding the 'prefix-' to every class name in the CSS:
Post CSS Prefixer
However, this does not modify the JavaScript (JSX), so the React components are still using the unnamed classes as className attributes.
Is there a way to use Rollup to automatically modify className attributes to include the same prefix specified in the CSS?
Note that I'm not looking for a fully modular solution such as CSS Modules, as I want the same 'prefix-' across every component inside the library.
You can't use static classNames to use this feature. To use it you need import style as object and assign it as object also.
import React from "react";
import style from "style.css";
class DivMyStyle extends React.Component {
render() {
return <div className={style.myStyle} />
}
}

Resources