Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react-native-web support #45

Open
vitalyiegorov opened this issue Sep 13, 2019 · 12 comments
Open

react-native-web support #45

vitalyiegorov opened this issue Sep 13, 2019 · 12 comments

Comments

@vitalyiegorov
Copy link

vitalyiegorov commented Sep 13, 2019

Hi @kristerkari,

Thank you for your plugin it is working great for React native projects,

We are trying to share our codebase across react-native and web(using react-native-web), our codebase is originally developed for React native CLI project, now we would like to add web support and using CRA with CRA-rewired but we are facing a problem:

According to CRA docs if you want to import SVG as react component you should use:
import { ReactComponent as Logo } from './logo.svg';

but SVGR is making only export default SvgComponent so we cannot use this approach, to fix this we could add a small fix here which should not break anything:

var jsCode = svgr.sync(src, svgrConfig) + '\nexport const ReactComponent = SvgComponent;';

which gives us needed support to work in react-native and web projects(also definitions.d.ts should be updated accordingly).

Maybe we could make a PR for this, or what do you think?

Here is a quick fix for postinstall npm/yarn script:

"postinstall": "sed -e \"s:var jsCode = svgr.sync(src, svgrConfig);:var jsCode = svgr.sync(src, svgrConfig) + 'export const ReactComponent = SvgComponent;';:g\" -i.bak ./node_modules/react-native-svg-transformer/index.js"
@kristerkari
Copy link
Owner

Not sure what library/plugin CRA is using for the SVG images, but the idea with this transformer is that you can use SVGR in you Web projects, and the images and properties will work 100% the same between Web and React Native.

@kristerkari
Copy link
Owner

kristerkari commented Sep 13, 2019

Can SVGR easily be added to CRA? That would fix the problem for you.

The fix that you are suggesting is a breaking change, so I'm not that keen on making it if it can be avoided.

@vitalyiegorov
Copy link
Author

Why do you think that this is a breaking change? It would leave default export SvgComponent and same usage import Logo from './logo.svg'; unchanged, but also it will give ability to use named export: import {ReactComponent as Logo} from './logo.svg'; which is supported by default by CRA.

CRA uses SVGR but with some modifications, I am just trying to enable the simple bridge to use this plugin with react-native-web and CRA.

@kristerkari
Copy link
Owner

Why do you think that this is a breaking change? It would leave default export SvgComponent and same usage import Logo from './logo.svg'; unchanged

True, that would not be a breaking change, but the Typescript types will not be compatible with CRA as in CRA the default import seems to be a string, and the named import is a React component.

I need to look into the CRA docs a bit more to see if I'm looking at the correct things.

@kristerkari
Copy link
Owner

Actually, I'm not completely sure if the default import in CRA is the url or not, I tried to look at this, but I'm not sure:
facebook/create-react-app#3722

@vitalyiegorov
Copy link
Author

vitalyiegorov commented Sep 16, 2019

@kristerkari Referring to CRA docs, you could use default export for SVG usage as image source:

import Logo from './logo.svg';
...
return <image src={Logo}/>

or use SVG as react component:

import {ReactComponent as Logo} from './logo.svg';
...
return <Logo/>

@kristerkari
Copy link
Owner

Yes exactly, and that's the problem: it works differently from the changes that you are suggesting. Which means that the default import would return a component in React Native, but a url in Web.

@vitalyiegorov
Copy link
Author

vitalyiegorov commented Sep 16, 2019

Yes, but this transformer is not used in any CRA projects, they have their own configuration and transformer, its recommended to use this transformer only with React Native CLI projects, so this modification will influence only React Native projects which are using this transformer and thus adding named export won't break any existing React Native projects because the case with returning default as a string is not used in React Native projects, but this will give the ability to add react-native-web support to existing React Native CLI projects by changing default imports to named imports.

@kristerkari
Copy link
Owner

kristerkari commented Sep 16, 2019

this modification will influence only React Native projects which are using this transformer and thus adding named export won't break any existing React Native projects because the case with returning default as a string is not used in React Native projects, but this will give the ability to add react-native-web support to existing React Native CLI projects by changing default imports to named imports.

I get that, but the change that you are suggesting also means that there would be types mismatch when sharing the same code between React Native and React Web with CRA and using Typescript or Flow.

I understand that many people are using CRA as their boilerplate, but what they do for Web doesn't make sense on the React Native side.

I use Typescript in most of my projects, and I'm not keen on making a change that only partially matches what CRA is doing, as all the existing users of this library are already using the default import for the React component.

I would suggest that you try to use @svgr/webpack directly instead, like it's set up in the demo project:
https://github.com/kristerkari/react-native-svg-example/blob/master/webpack.config.js#L28-L38

@vomchik
Copy link

vomchik commented Feb 6, 2020

@vitalyiegorov You can set a custom template for SVGR.

// svgr.config.js

const isExportNamedDeclaration = item => item.type === "ExportNamedDeclaration";

const hasExportNamedDeclaration = (items) => {
  if (items === null) {
    return false;
  }

  if (items instanceof Array) {
    return !!(items && items.find(isExportNamedDeclaration))
  }

  return isExportNamedDeclaration(items) 
}

const getName = (component) => {
  if (component instanceof Object) {
    return component.name;
  }

  return component;
}

const customTemplate = (
  { template },
  opts,
  { imports, componentName, props, jsx, exports },
) => {
  const ReactComponent = hasExportNamedDeclaration(exports) 
    ? ''
    : 'export const ReactComponent = ' + getName(componentName);

  return template.ast`
    ${imports}
    function ${componentName}(${props}) {
      return ${jsx};
    }
    ${ReactComponent}
    ${exports}
  `
}

module.exports = {
  template: customTemplate
}

@kristerkari Maybe it will be useful, so let's add it to the README file.

@calebmackdavenport
Copy link

@vitalyiegorov You can set a custom template for SVGR.

// svgr.config.js

const isExportNamedDeclaration = item => item.type === "ExportNamedDeclaration";

const hasExportNamedDeclaration = (items) => {
  if (items === null) {
    return false;
  }

  if (items instanceof Array) {
    return !!(items && items.find(isExportNamedDeclaration))
  }

  return isExportNamedDeclaration(items) 
}

const getName = (component) => {
  if (component instanceof Object) {
    return component.name;
  }

  return component;
}

const customTemplate = (
  { template },
  opts,
  { imports, componentName, props, jsx, exports },
) => {
  const ReactComponent = hasExportNamedDeclaration(exports) 
    ? ''
    : 'export const ReactComponent = ' + getName(componentName);

  return template.ast`
    ${imports}
    function ${componentName}(${props}) {
      return ${jsx};
    }
    ${ReactComponent}
    ${exports}
  `
}

module.exports = {
  template: customTemplate
}

@kristerkari Maybe it will be useful, so let's add it to the README file.

Can you add this into the root of your project to handle the SVGs for web? Do you need to do anything else?

@janlat
Copy link

janlat commented Feb 4, 2022

Since SVGR v6.0.0, switching to named exports is just a matter of adding exportType: 'named' to your svgrrc file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants