3

What I want to accomplish is this: I have created button component based on TouchableOpacity. In my app I have 3 types of differently looking buttons, they all share some styles and have something specific as well. Below is how my Button.js looks like:

import React, { Component } from 'react';
import { Text, TouchableOpacity } from 'react-native';

class Button extends Component {
  render() {
    const { children, onPress, style } = this.props;
    const { buttonStyle, textStyle } = styles;

    return (
      <TouchableOpacity onPress={onPress} style={[buttonStyle]}>
        <Text style={[textStyle]}>
          {children}
        </Text>
      </TouchableOpacity>
    );
  }
}

//Commonly shared button styles
const styles = {
  textStyle: {
    alignSelf: 'center',
    fontSize: 17,
    paddingTop: 15,
    paddingBottom: 15
  },
  buttonStyle: {
    alignSelf: 'stretch',
    marginLeft: 15,
    marginRight: 15
  }
};

//Below are specific appearance styles
const black = {
  container: {
    backgroundColor: '#000'
  },
  text: {
    color: '#FFF'
  }
};

const white = {
  container: {
    backgroundColor: '#FFF'
  },
  text: {
    color: '#000'
  }
};

It would be great if I could use this button something like this:

<Button onPress={} style='black'>PLAY</Button>
<Button onPress={} style='white'>CANCEL</Button>

I.e. default buttonStyle and textStyle are applied from styles object. And I just want to pass a single word ('black', 'white') to reference additional style objects described in Button component.

I know I can create a helper method with switch, but I think there is a shorter way to do this. Is there?

Thanks a lot!

realvadim
  • 154
  • 12
  • You don't need to put your styles in an array if it's only one – Dominic Dec 23 '16 at 13:33
  • May be this post will help you. http://stackoverflow.com/a/31638988/4361743 – karman Dec 23 '16 at 13:37
  • I would make separate components WhiteButton and BlackButton, based on Button, so your Button component will stay clean of any other logic except of it's own. Even better names would be PrimaryButton and SecondaryButton not to make names specific to their style. Button component will provide a basic functionality of a button - it can be pressed, container and text can be styled in any way. You'll be able to reuse it to create more complex components, because a lot of things can be pressed, but they might look very different. – Michael Radionov Dec 23 '16 at 14:25
  • @DominicTobias Thanks. I was just playing around to black and white styles as well. – realvadim Dec 26 '16 at 09:12
  • @karman Thank you! – realvadim Dec 26 '16 at 09:17
  • @MichaelRadionov Thanks a lot! – realvadim Dec 26 '16 at 09:18

3 Answers3

2

I think this would be the shortest and cleanest way

import React, { PropTypes } from 'react';
import { Text, TouchableOpacity } from 'react-native';

const Button = ({ children, onPress, type }) => (
  <TouchableOpacity onPress={onPress} style={[styles.defaultButton, styles[type].button]}>
    <Text style={[styles.defaultText, styles[type].text]}>
      {children}
    </Text>
  </TouchableOpacity>
);

Button.propTypes = {
  children: PropTypes.node.isRequired,
  onPress: PropTypes.func.isRequired,
  type: PropTypes.oneOf(['white', 'black']).isRequired,
};

const styles = {
  defaultButton: {
    alignSelf: 'stretch',
    marginLeft: 15,
    marginRight: 15
  },
  defaultText: {
    alignSelf: 'center',
    fontSize: 17,
    paddingTop: 15,
    paddingBottom: 15
  },
  white: {
    button: {
      backgroundColor: '#FFF'
    },
    text: {
      color: '#000'
    }
  },
  black: {
    button: {
      backgroundColor: '#000'
    },
    text: {
      color: '#FFF'
    }
  },
};

export default Button;

Add type && style[type].button if type is not required prop. Like this:

const Button = ({ children, onPress, type }) => (
  <TouchableOpacity onPress={onPress} style={[styles.defaultButton, type && styles[type].button]}>
    <Text style={[styles.defaultText, type && styles[type].text]}>
      {children}
    </Text>
  </TouchableOpacity>
);

Button.propTypes = {
  children: PropTypes.node.isRequired,
  onPress: PropTypes.func.isRequired,
  type: PropTypes.oneOf(['white', 'black']),
};
Henrik R
  • 4,742
  • 1
  • 24
  • 23
  • Thanks for your help. I will be using this solution. Can you please explain, what does the block Button.propTypes = { ... } do? I have tried doing it without it and it still works fine. Moreover, what does 'isRequired' change? I mean whether I have it required or not, I still see app crashing if I pass 'blaaack' or nothing at all, i.e. if it is undefined. – realvadim Dec 26 '16 at 09:37
  • Good that the solution was useful. You should always add propTypes to your React components so other developers see what kind of data the component should receive. You should definitely read this because every React developer must use propTypes https://facebook.github.io/react/docs/typechecking-with-proptypes.html :) – Henrik R Dec 26 '16 at 17:42
1

As per my understanding towards your questions,please have a look at this:-

var styleChangeForButton,styleChangeForText
class Button extends Component {
  constructor(props)
  {
    super(props)
    const { children, onPress, style } = this.props;
    const { buttonStyle, textStyle } = styles;
    styleChangeForButton = [buttonStyle]
    styleChangeForText = [textStyle]

  }
  onPress()
  {
       styleChangeForButton = 'black'
       styleChangeForText = 'white'
  }

  render() {

  //

    style
    return (
      <TouchableOpacity onPress={onPress} style={styleChangeForButton}>
        <Text style={styleChangeForText}>
          {children}
        </Text>
      </TouchableOpacity>
    );
  }
}

//Commonly shared button styles
const styles = {
  textStyle: {
    alignSelf: 'center',
    fontSize: 17,
    paddingTop: 15,
    paddingBottom: 15
  },
  buttonStyle: {
    alignSelf: 'stretch',
    marginLeft: 15,
    marginRight: 15
  }
};

//Below are specific appearance styles
const black = {
  container: {
    backgroundColor: '#000'
  },
  text: {
    color: '#FFF'
  }
};

const white = {
  container: {
    backgroundColor: '#FFF'
  },
  text: {
    color: '#000'
  }
};
Codesingh
  • 3,316
  • 1
  • 11
  • 18
1

You could do something like this:

import React, { PropTypes } from 'react';
import { StyleSheet, Text, TouchableOpacity } from 'react-native';

// Commonly shared button styles
const defaultStyle = StyleSheet.create({
  textStyle: {
    alignSelf: 'center',
    fontSize: 17,
    paddingTop: 15,
    paddingBottom: 15,
  },
  buttonStyle: {
    alignSelf: 'stretch',
    marginLeft: 15,
    marginRight: 15,
  },
});

// Below are specific appearance styles
const black = StyleSheet.create({
  container: {
    backgroundColor: '#000',
  },
  text: {
    color: '#FFF',
  },
});

const white = StyleSheet.create({
  container: {
    backgroundColor: '#FFF',
  },
  text: {
    color: '#000',
  },
});

const themes = {
  black,
  white,
};

function Button({ children, onPress, theme }) {
  const buttonStyles = [defaultStyle.buttonStyle];
  const textStyles = [defaultStyle.textStyle];

  if (theme) {
    buttonStyles.push(themes[theme].container);
    textStyles.push(themes[theme].text);
  }

  return (
    <TouchableOpacity onPress={onPress} style={buttonStyles}>
      <Text style={textStyles}>
        {children}
      </Text>
    </TouchableOpacity>
  );
}

Button.propTypes = {
  onPress: PropTypes.func.isRequired,
  theme: PropTypes.string,
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node,
  ]),
};

export default Button;
Dominic
  • 62,658
  • 20
  • 139
  • 163