Skip to content

Instantly share code, notes, and snippets.

@RafalFilipek
Last active January 5, 2017 20:25
Show Gist options
  • Save RafalFilipek/49551c742295b75875815e1d02c72125 to your computer and use it in GitHub Desktop.
Save RafalFilipek/49551c742295b75875815e1d02c72125 to your computer and use it in GitHub Desktop.

In my opinion describing presentation components with simple props is more readable. For example:

// :)
<Button blue big />
// :/
<Button color="blue" size="big" />

I agree that setting things as key=value allow us to validate them or set up default values, like:

Button.propTypes = {
  color: React.PropTypes.oneOf(['blue', 'red', 'green']),
  size: React.PropTypes.oneOf(['small', 'medium', 'big']),
};
Button.defaultProps = {
  color: 'blue',
  size: 'medium',
};
//...
<Button color="blue" size="big" />

But I still like <Button big red /> :)

So I was thinking how to do it. Let's say we have our Button component and we want to control size and color props. So we can define two maps:

const fontSizes = {
  small: 10,
  medium: 15,
  big: 20,
};
const colors = {
  blue: 'blue',
  red: 'red',
  green: 'green',
};

Keep in mind that you can store those maps in separated modules and access them anywhere.

Now let's write out styled Button component:

const Button = styled.button`
  padding: 10px;
  border: 1px solid #eee;
  background-color: ${???};
  border-radius: 3px;
  font-size: ${???}px;
`;

Cool! Now we want to define both background-color and font-size for two cases:

  1. Property is defined.
  2. Property is not defined so:
    1. We have some default value.
    2. We just pass.

First idea:

background-color ${any(colors, 'white')}

So we provide map and default key. I know any is not the best idea :) Here it is:

const any = (map, fallback) => ((props) => {
  const keyFromProps = Object.keys(map).find(key => props[key]);
  return map[keyFromProps || fallback];
});

This function will search for first existing key in map (for example colors). When no key was found fallback key will be used.

But there is a problem. Default value is set inside styles definition (this white word).

So second version:

font-size: ${any2(fontSizes, 'size')}px;
const any2 = (map, fallback) => ((props) => {
  const keyFromProps = Object.keys(map).find(key => props[key]);
  return map[keyFromProps || props[fallback]];
});

Looks almost the same ;) But now second argument is name of the prop that will provide default value. So now you can have something like:

Button.defaultProps = {
  size: 'big',
};
//...
any2(fontSizes, 'size')

The cool thing is that you can now have:

<Button small blue />
// and
<Button size="small" color="blue" />

What you think? Is there any better way to do this?

import React from 'react';
import { render } from 'react-dom';
import styled from 'styled-components';
const fontSizes = {
small: 10,
medium: 15,
big: 20,
};
const colors = {
white: 'white',
red: 'red',
blue: 'blue',
green: 'green',
}
const any = (map, fallback) => ((props) => {
const keyfromProps = Object.keys(map).find(key => props[key]);
return map[keyfromProps || fallback];
});
const any2 = (map, fallback) => ((props) => {
const keyfromProps = Object.keys(map).find(key => props[key]);
return map[keyfromProps || props[fallback]];
});
const Button = styled.button`
padding: 10px;
border: 1px solid #eee;
background-color: ${any2(colors, 'white')};
border-radius: 3px;
font-size: ${any2(fontSizes, 'size')}px;
`;
Button.defaultProps = {
size: 'big',
color: 'red',
}
export default class App extends React.Component {
render() {
return (
<div>
<Button blue small>Hello</Button>
<Button green>Hello</Button>
<Button>Hello</Button>
</div>
);
}
}
render(<App/>, document.querySelector('#root'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment