Skip to content

Instantly share code, notes, and snippets.

@lintonye
Last active March 1, 2019 22:46
Show Gist options
  • Save lintonye/8e4d47dfb3089eae2462c5acced0030e to your computer and use it in GitHub Desktop.
Save lintonye/8e4d47dfb3089eae2462c5acced0030e to your computer and use it in GitHub Desktop.
React Recipes

Render svg on Sketch, Web and React Native

What?

Use a single codebase to render svg on Sketch, Web and React Native, but react-primitives doesn't support svg yet. One suggestion is to use react-primitives-svg, but there's an issue when trying to render it on the web, as described in airbnb/react-sketchapp#265.

Solution

We could directly use the core.web.js file as below. Also there might be a way to pick up the .web.js extension just as .android.js and .ios.js.

// Svg.js
import { Platform } from "react-primitives";

let SvgLib;

switch (Platform.OS) {
  case "sketch":
    SvgLib = require("react-primitives-svg/lib/core.sketch");
    break;
  case "web":
    SvgLib = require("react-primitives-svg/lib/core.web");
    break;
  default: // React Native should be able to pick up `core.ios.js` or `core.android.js` according to the platform
    SvgLib = require("react-primitives-svg");
}

export const { Svg, Path } = SvgLib;
// App.js
import { Svg, Path } from './Svg';
...
const App = () => (
  <View>
      <Svg viewBox="0 0 24 24" style={{ fill: 'blue'}}>
        <Path d='M23.44 4.83c-.8.37-1.5.38-2.22.02.93-.56.98-.96 1.32-2.02-.88.52-1.86.9-2.9 1.1-.82-.88-2-1.43-3.3-1.43-2.5 0-4.55 2.04-4.55 4.54 0 .36.03.7.1 1.04-3.77-.2-7.12-2-9.36-4.75-.4.67-.6 1.45-.6 2.3 0 1.56.8 2.95 2 3.77-.74-.03-1.44-.23-2.05-.57v.06c0 2.2 1.56 4.03 3.64 4.44-.67.2-1.37.2-2.06.08.58 1.8 2.26 3.12 4.25 3.16C5.78 18.1 3.37 18.74 1 18.46c2 1.3 4.4 2.04 6.97 2.04 8.35 0 12.92-6.92 12.92-12.93 0-.2 0-.4-.02-.6.9-.63 1.96-1.22 2.56-2.14z' />
      </Svg>
  </View?
);

export default App;

How to debug "undefined is not an object" error?

The error

error

The code

const buttonColors = {
  pink: {
    background: '#fff',
    border: '#D82662',
    text: '#D82662'
  },
  pinkFill: {
    background: '#D82662',
    border: '#D82662',
    text: '#fff'
  },
  noFill: {
    background: '#fff',
    border: '#fff',
    text: '#D82662'
  },
  grey: {
    background: '#fff',
    border: '#D5D5D5',
    text: '#707070'
  }
};

const getBackgroundColor = color => buttonColors[color].background;
const getBorderColor = color => buttonColors[color].border;
const getTextColor = color => buttonColors[color].text;

const ButtonContainer = styled.View`
  border: 1px solid ${props => getBorderColor(props.color)};
  border-radius: 5px;
  background: ${props => getBackgroundColor(props.color)};
  padding: 12px 20px;
  margin: 0px 0px 10px;
  flex: 1;
  flex-basis: 100%;
`; 

const ButtonText = styled.Text`
  color: red;
  color: ${props => getTextColor(props.color)};
  font-family: 'OpenSans-Bold';
  font-size: 16px;
  line-height: 22px;
  text-align: center;
  width: 160px;
`; 

export default class Button extends Component {
  render() {
    return (
      <ButtonContainer>
        <ButtonText>
          {this.props.children}
        </ButtonText>
      </ButtonContainer>
    );
  }
}

How to debug?

Starting from the error message:

TypeError: undefined is not an object (evaluating 'buttonColors[color].background`)

This means, at some point in the code, the value of buttonColors[color] is undefined, but we are trying to use it as an object -- we are trying to get its background property with .background.

Searching in the code, it's this line:

const getBackgroundColor = color => buttonColors[color].background;

This is an arrow function named getBackgroundColor. Who calls it?

Keep searching... This is the only place where the method is called:

const ButtonContainer = styled.View`
  ...
  background: ${props => getBackgroundColor(props.color)};
  ...
`; 

Here, props.color is passed into getBackgroundColor and used as the color in buttonColors[color].background.

What's its value? Let's find out!

We'll use console.log to print out the value of props.color and see if we could find any clue.

const ButtonContainer = styled.View`
  ...
  background: ${props => { 
    console.log('=========> props.color=', props.color); 
    return getBackgroundColor(props.color)}; 
  }
  ...
`;

If you run it, you'll see this in the console output (see here if you are using React Sketch.app)

=========> props.color=undefined

Alright, so basically we pass an undefined into getBackgroundColor. What is the value of buttonColors[undefined]? It's undefined as well, right?

But wait a second, why is props.color undefined? The question is, what is this props? It's the props we give to the ButtonContainer component, right?

However, look at how we use the ButtonContainer component:

      <ButtonContainer>
        <ButtonText>
          {this.props.children}
        </ButtonText>
      </ButtonContainer>

What are the props of ButtonContainer? Nothing! There are no props whatsoever. There is no doubt that props.color is undefined.

How to fix it?

I'll leave it to you. There will be another similar error in this code as well, fix it in the same way.

Debugging summary

  • Don't panic
  • Read the error message carefully and try to make sense of it
  • Follow the code execution backwards, search in the code
  • console.log

How to use saleforces' lighthouse design system

lintonye/design-system-react#1

@LincMitch
Copy link

I just added "react-primitives-svg": "0.0.2", to dependencies in the package.json file and now have another error:
Can't resolve './core'

@LincMitch
Copy link

LincMitch commented Feb 18, 2018

Actually, I'm not sure if I manually add dependencies or wether I have to install via the terminal npm install react-primitives-svg.
Sorry, I have so many questions. I'm like a fish out of water.

@lintonye
Copy link
Author

npm install --save react-primitives-svg

@LincMitch
Copy link

LincMitch commented Feb 20, 2018

You legend! I finally got it working thats to your code.
It renders to Web and Sketch, just not Native yet.

I think the issue was over dependencies and possible version conflicts. This setup seemed to work:

"chroma-js": "^1.2.2", "nwb": "^0.15.6", "react": "^16.2.0", "react-dom": "^16.2.0", "react-native": "^0.53.2", "react-native-svg": "^6.2.1", "react-primitives": "^0.5.0", "react-primitives-svg": "0.0.3", "react-sketchapp": "^1.0.0", "react-test-renderer": "^16.2.0", "styled-components": "^3.1.6", "webpack": "^3.11.0"

But I don't know anything about nwb and can't see whychroma-jswas needed.

The errors below may have also been related to me editing the package.json file directly.

@LincMitch
Copy link

Almost there.

Now the SVG height and width seem to be interpreted differently for Sketch and web.
<Svg viewBox="0 0 200 200" width="200" height="200" fill="blue"> is more like 20/20 on web, but OK in Sketch.
but...
<Svg viewBox="0 0 200 200" fill="pink"> renders 95.61px /.47px in Sketch, but OK on web.

I think I saw this issue someplace, if I find it, I'll add a link here.

@LincMitch
Copy link

Maybe a yoga issue.
airbnb/react-sketchapp#269

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment