59

I'd like the style of a button in my app to change when it is being pressed. What is the best way to do this?

domi91c
  • 1,962
  • 4
  • 23
  • 38
  • Do you need it to change while it is being pressed only, and change back when the user is not pressing it? Or do you need to change it when it is touched, and keep the changed state after the touch is stopped? – Nader Dabit Jan 06 '16 at 12:58
  • To change back when the user is not pressing it. – domi91c Jan 06 '16 at 14:40
  • This [solution](https://stackoverflow.com/questions/34088496/how-to-change-image-and-text-color-when-clicking-using-react-native/52155810#52155810) worked on me – Maybelle Pacate Nov 06 '18 at 08:50

5 Answers5

65

Use TouchableHighlight.

Here an example:

enter image description here

import React from 'react';
import { TouchableHighlight, View, Text, StyleSheet } from 'react-native';

export default function Button() {

  var [ isPress, setIsPress ] = React.useState(false);

  var touchProps = {
    activeOpacity: 1,
    underlayColor: 'blue',                               // <-- "backgroundColor" will be always overwritten by "underlayColor"
    style: isPress ? styles.btnPress : styles.btnNormal, // <-- but you can still apply other style changes
    onHideUnderlay: () => setIsPress(false),
    onShowUnderlay: () => setIsPress(true),
    onPress: () => console.log('HELLO'),                 // <-- "onPress" is apparently required
  };

  return (
    <View style={styles.container}>
      <TouchableHighlight {...touchProps}>
        <Text>Click here</Text>
      </TouchableHighlight>
    </View>
  );
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  btnNormal: {
    borderColor: 'blue',
    borderWidth: 1,
    borderRadius: 10,
    height: 30,
    width: 100,
  },
  btnPress: {
    borderColor: 'blue',
    borderWidth: 1,
    height: 30,
    width: 100,
  }
});
Besart Hoxhaj
  • 917
  • 1
  • 8
  • 12
  • This is really helpful! And `onHideUnderlay` & `onShowUnderlay` methods are not conflict with `onPress`. This kinda get me confused a little. – Bruce Lee Sep 24 '16 at 12:11
  • 7
    Just a note that `` must also contain an `onPress` handler for this to work (as of React Native v0.34) – rnevius Oct 08 '16 at 13:33
  • Did not know about `onHideUnderlay` & `onShowUnderlay`! Was googling forever to find out how to accomplish something like this. Thank you! – Greg Blass Oct 25 '17 at 19:28
  • Using this method, the style changes only after you hold your fingers on the button ... it will not change only on a tap. – P.Lorand Nov 08 '17 at 17:01
  • 1
    Important to note that `underlayColor` is required along with `activeOpacity={1}` for this to work. – instanceof Dec 19 '19 at 09:38
  • how did you get the text color to turn white on click? –  Aug 07 '21 at 10:52
30

React Native now provides a new Pressable component that can detect various stages of press interactions. So, in order to change the color(in general any style) of the component, refer below example:

<Pressable
  style={({ pressed }) => [{ backgroundColor: pressed ? 'black' : 'white' }, styles.btn ]}>
  {({ pressed }) => (
    <Text style={[{ color: pressed ? 'white' : 'black' }, styles.btnText]}>
      {text}
    </Text>
  )}
</Pressable>

Code breakdown:

style={({ pressed }) => [{ backgroundColor: pressed ? 'black' : 'white' }, styles.btn ]}

Here the style prop receives pressed(boolean) that reflects whether Pressable is pressed or not and returns an array of styles.

{({ pressed }) => (
    <Text style={[{ color: pressed ? 'white' : 'black' }, styles.btnText]}>
      {text}
    </Text>
)}

Here the text style too can be modified as the pressed is also accessible to the children of Pressable component.

Ankush Chauhan
  • 1,433
  • 2
  • 17
  • 22
16

Use the prop:

underlayColor

<TouchableHighlight style={styles.btn} underlayColor={'gray'} />

https://reactnative.dev/docs/touchablehighlight

TheScrappyDev
  • 4,375
  • 2
  • 21
  • 25
  • 1
    `underlayColor`will be displayed with a slight opacity. And setting `activeOpacity` to 0 will completely ignore any styles applied to the unpressed state. – Naoto Ida Jun 29 '17 at 06:26
2

This is Besart Hoxhaj's answer in ES6. When i answer this, React Native is 0.34.

 import React from "react";
 import { TouchableHighlight, Text, Alert, StyleSheet } from "react-native";

 export default class TouchableButton extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        pressed: false
    };
}
render() {
    return (
        <TouchableHighlight
            onPress={() => {
                // Alert.alert(
                //     `You clicked this button`,
                //     'Hello World!',
                //     [
                //         {text: 'Ask me later', onPress: () => console.log('Ask me later pressed')},
                //         {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
                //         {text: 'OK', onPress: () => console.log('OK Pressed')},
                //     ]
                // )
            }}
            style={[
                styles.button,
                this.state.pressed ? { backgroundColor: "green" } : {}
            ]}
            onHideUnderlay={() => {
                this.setState({ pressed: false });
            }}
            onShowUnderlay={() => {
                this.setState({ pressed: true });
            }}
        >
            <Text>Button</Text>
        </TouchableHighlight>
    );
}
}

const styles = StyleSheet.create({
button: {
    padding: 10,
    borderColor: "blue",
    borderWidth: 1,
    borderRadius: 5
}
});
Bruce Lee
  • 4,177
  • 3
  • 28
  • 26
0

use something like that :

class A extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      onClicked: false
    }
    this.handlerButtonOnClick = this.handlerButtonOnClick.bind(this);
  }
  handlerButtonOnClick(){
    this.setState({
       onClicked: true
    });
  }
  render() {
    var _style;
    if (this.state.onClicked){ // clicked button style
      _style = {
          color: "red"
        }
    }
    else{ // default button style
      _style = {
          color: "blue"
        }
    }
    return (
        <div>
            <button
                onClick={this.handlerButtonOnClick}
                style={_style}>Press me !</button>
        </div>
    );
  }
}

If you use an external CSS, you can use className in place of style property :

render() {
    var _class = "button";
    var _class.concat(this.state.onClicked ? "-pressed" : "-normal") ;
    return (
        <div>
            <button
                onClick={this.handlerButtonOnClick}
                className={_class}>Press me !</button>
        </div>
    );
  }

It doesn't really matter how do you apply your CSS. Keep your eyes on the "handlerButtonOnClick" method.

When the state change, the component is re-rendered ("render" method is called again).

Good luck ;)

  • react-native doesn't have a button component. It uses touchableHighlight, touchableNativeFeedback among others. – Nico Mar 19 '16 at 17:03
  • the problem is not about react-native or not... the problem is about to understand how & when react apply styles on component. The flow is always the same : handleEvent -> handler -> setState -> render. My answer is about this flow, not about react-native – sami ghazouane Mar 30 '16 at 09:26
  • 1
    Update - React Native added the [``](https://facebook.github.io/react-native/docs/button.html) component in [version 0.37](https://github.com/facebook/react-native/releases/tag/v0.37.0) – Hastig Zusammenstellen Jun 06 '18 at 10:33