Una vez que su primera aplicación React Native ha sido creada y está en funcionamiento, es posible que desee reutilizar ciertos elementos como componentes funcionales. Estos componentes pueden configurarse para otras aplicaciones y compartirse como una biblioteca.
Descripción
En este tutorial, vamos a empezar con una aplicación sencilla que contiene cuatro botones. Vamos a crear un componente funcional configurable de React Native en un archivo fuente que reutilizaremos en el archivo principal de la aplicación.
Veremos cómo:
- pasar propiedades a un componente
- recuperar datos de componentes a partir de eventos
- utilizar un componente definido en un archivo fuente en el archivo principal
Aplicación básica: 4 botones
En este código, tenemos una vista principal (View) que cubre la pantalla, el título de la aplicación, y la vista que contiene los 4 botones. Las funciones que se realizan al pulsar los botones y los estilos definidos.
/**
 * https://github.com/jim-at-jibba/react-native-game-pad
 * https://github.com/ionkorol/react-native-joystick
 * 
 */
import React from "react";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
const GamePad = () =>  {
  let btnAColor = "red",
      btnBColor = "orange",
      btnXColor = "royalblue",
      btnYColor = "limegreen"
  const onButtonAPress = () => {
    console.log('You clicked button A');
  };
  const onButtonBPress = () => {
    console.log('You clicked button B');
  };
  const onButtonXPress = () => {
    console.log('You clicked button X');
  };
  const onButtonYPress = () => {
    console.log('You clicked button Y');
  };
 
    return (
      <View style={styles.mainBody}>
          <Text
            style={styles.mainTitle}>
            AC Controller
          </Text>
          <View style={{ flex: 1,justifyContent: "center", alignItems: "center",}}>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"green",
                  zIndex: 100,
                }}
              >
                <TouchableOpacity
                  style={[styles.button, {  top:"30%",backgroundColor: `${btnXColor}` }]} //top:"30%", right:"1%", 
                  onPress={() => onButtonXPress()}
                >
                  <Text style={styles.buttonText}>X</Text>
                </TouchableOpacity>
              </View>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"gold",
                }}
              >
                <TouchableOpacity
                  style={[styles.button, { top: "50%", right:"20%", backgroundColor: `${btnYColor}` }]} //top:"2%", left:"10%",
                  onPress={() => onButtonYPress()}
                >
                  <Text style={styles.buttonText}>Y</Text>
                </TouchableOpacity>
              </View>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"blue",
                }}
              >
                <TouchableOpacity
                  style={[styles.button, { bottom:"50%", left:"20%", backgroundColor: `${btnAColor}` }]} //bottom:"2%", 
                  onPress={() => onButtonAPress()}
                >
                  <Text style={styles.buttonText}>A</Text>
                </TouchableOpacity>
              </View>
            <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"red",
                }}
              >
                <TouchableOpacity
                  style={[styles.button, { bottom:"30%", backgroundColor: `${btnBColor}` }]} //bottom:"30%", left:"1%",
                  onPress={() => onButtonBPress()}
                >
                  <Text style={styles.buttonText}>B</Text>
                </TouchableOpacity>
              </View>
          </View>
      </View>            
    );
}
export default GamePad;
//parameters
let BACKGROUND_COLOR = "#161616"; //191A19
let BUTTON_COLOR = "#346751"; //1E5128
let ERROR_COLOR = "#C84B31"; //4E9F3D
let TEXT_COLOR = "#ECDBBA"; //D8E9A8
const styles = StyleSheet.create({
  mainBody: {
    flex:1,
    justifyContent: 'center',
    alignItems: "center",
    color:TEXT_COLOR,
    backgroundColor: BACKGROUND_COLOR,
  },
  mainTitle:{
    color: TEXT_COLOR,
    fontSize: 30,
    textAlign: 'center',
    borderBottomWidth: 2,
    borderBottomColor: ERROR_COLOR,
    width:"100%"
  },
  button: {
    height: 90,
    width: 90,
    borderRadius: 90 / 2,
    justifyContent: "center",
    alignItems: "center"
  },
  buttonText: {
    fontSize: 22,
    color: "white",
    fontWeight: "700"
  },
});
al pulsar los botones se ejecutan correctamente las funciones del evento
 BUNDLE  ./index.js
 LOG  Running "CustomApp" with {"rootTag":31}
 LOG  You clicked button Y
 LOG  You clicked button X
 LOG  You clicked button A
 LOG  You clicked button B
Llamar al componente desde un archivo fuente
Vamos a colocar este código en un archivo .
Para poder utilizarla, utilizamos la palabra clave export para definir la función
export const ButtonPad = ()  =>  {
...
}
A continuación, el componente puede utilizarse importándolo
import {ButtonPad} from "./src/ACJoysticks";
const GamePad = () =>  {
    return (
      <View style={styles.mainBody}>
          <Text
            style={styles.mainTitle}>
            AC Controller
          </Text>
          <ButtonPad/>
      </View>            
    );
};
export default GamePad;
Pasar propiedades a un componente funcional
Para que el componente sea configurable, definimos parámetros en el fichero principal que se pasarán a las propiedades del componente. Las propiedades que queremos pasar al componente son:
- el texto que aparece en los botones
- el color de los botones
- el tamaño de los botones
- la función onClick, que gestiona todos los botones
Los parámetros del componente se colocan en un ButtonPadProps tipo
type ButtonPadProps = {
  radius?: number;
  names?: string[4];
  colors?: string[4];
  onClick?:(evt: ButtonPadEvent) => void;
};
A continuación, podemos definir estas propiedades como entrada del componente y llamarlas con valores por defecto.
export const ButtonPad = (props : ButtonPadProps)  =>  {
  const { onClick, radius = 45, names= ['X','Y','A','B'], colors = ["royalblue","limegreen","red","orange"] } = props;
Definimos una única función para llamar a los botones
  const onButtonPress = (index:number) => {
    console.log('You clicked button ' + names[index]);
  };
Por último, modificamos el código para tener en cuenta las distintas propiedades.
/**
 * ButtonPad
 */
type ButtonPadProps = {
  radius?: number;
  names?: Array<string>;
  colors?: Array<string>;
  onClick?:(evt: ButtonPadEvent) => void;
};
export const ButtonPad = (props : ButtonPadProps)  =>  {
  const { onClick, radius = 45, names= ['X','Y','A','B'], colors = ["royalblue","limegreen","red","orange"] } = props;
  let textSize = radius;
  const onButtonPress = (index:number) => {
    console.log('You clicked button ' + names[index]);
  };
  
 
    return (
          <View style={{ flex: 1,justifyContent: "center", alignItems: "center",}}>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"green",
                  zIndex: 100,
                }}
              >
                <TouchableOpacity
                  style={[{
                    height: 2*radius,
                    width: 2*radius,
                    borderRadius: radius,
                    justifyContent: "center",
                    alignItems: "center"
                  }, {  top:"30%",backgroundColor: `${colors[0]}` }]} //top:"30%", right:"1%", 
                  onPress={() => onButtonPress(0)}
                >
                  <Text style={{
                            fontSize: textSize,
                            color: "white",
                            fontWeight: "700"
                          }}>{names[0]}</Text>
                </TouchableOpacity>
              </View>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"gold",
                }}
              >
                <TouchableOpacity
                  style={[{
                    height: 2*radius,
                    width: 2*radius,
                    borderRadius: radius,
                    justifyContent: "center",
                    alignItems: "center"
                  }, { top: "50%", right:"20%", backgroundColor: `${colors[1]}` }]} //top:"2%", left:"10%",
                  onPress={() => onButtonPress(1)}
                >
                  <Text style={{
                            fontSize: textSize,
                            color: "white",
                            fontWeight: "700"
                          }}>{names[1]}</Text>
                </TouchableOpacity>
              </View>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"blue",
                }}
              >
                <TouchableOpacity
                  style={[{
                    height: 2*radius,
                    width: 2*radius,
                    borderRadius: radius,
                    justifyContent: "center",
                    alignItems: "center"
                  }, { bottom:"50%", left:"20%", backgroundColor: `${colors[2]}` }]} //bottom:"2%", 
                  onPress={() => onButtonPress(2)}
                >
                  <Text style={{
                            fontSize: textSize,
                            color: "white",
                            fontWeight: "700"
                          }}>{names[2]}</Text>
                </TouchableOpacity>
              </View>
            <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"red",
                }}
              >
                <TouchableOpacity
                  style={[{
                    height: 2*radius,
                    width: 2*radius,
                    borderRadius: radius,
                    justifyContent: "center",
                    alignItems: "center"
                  }, { bottom:"30%", backgroundColor: `${colors[3]}` }]} //bottom:"30%", left:"1%",
                  onPress={() => onButtonPress(3)}
                >
                  <Text style={{
                            fontSize: textSize,
                            color: "white",
                            fontWeight: "700"
                          }}>{names[3]}</Text>
                </TouchableOpacity>
              </View>
          </View>
    );
}
Pasar datos del componente a la aplicación
Para devolver datos a la aplicación principal, utilizamos una interfaz que contiene los estados de los botones y el nombre del botón pulsado
interface ButtonPadEvent {
  states : Array<boolean>;
  pressed: string;
}
Modificamos la función onButtonPress para alimentar los estados y enviarlos a la función onClick.
  const onButtonPress = (index:number) => {
    let btnStates= [false,false,false,false];
    btnStates[index] = true;
    if(typeof(onClick)==="function"){
      onClick({
             states: btnStates,
             pressed: names[index]
         });
       }
  };
N.B.: La notación if(typeof(onClick)===»function»){ onClick() } es equivalente a onClick && onClick()
Ahora podemos llamar a nuestro componente en la aplicación principal, crear una función de escucha y modificar sus parámetros.
const GamePad = () =>  {
  const onPadClick = (data: any) => {
    console.log("form ButtonPad: ",data);
  }
    return (
      <View style={styles.mainBody}>
          <Text
            style={styles.mainTitle}>
            AC Controller
          </Text>
          <ButtonPad radius={60} names={['\u2573', '\u25EF', '\u25B3', "\u25A2"]} colors={['black', 'black', 'black', 'black']} onClick={onPadClick} />
      </View>            
    );
}
export default GamePad;
info Reloading app...
 BUNDLE  ./index.js
 LOG  Running "CustomApp" with {"rootTag":81}
 LOG  form ButtonPad:  {"pressed": "╳", "states": [true, false, false, false]}
 LOG  form ButtonPad:  {"pressed": "△", "states": [false, false, true, false]}
 LOG  form ButtonPad:  {"pressed": "▢", "states": [false, false, false, true]}
 LOG  form ButtonPad:  {"pressed": "◯", "states": [false, true, false, false]}
N.B.: para hacer las cosas correctamente, tendrías que crear los observadores onPressIn y onPressOut para poner los estados a cero y modificar la lógica para escuchar varios botones a la vez.
Código completo
./src/ACJoysticks.tsx
/**
 * ButtonPad
 */
interface ButtonPadEvent {
  states : Array<boolean>;
  pressed: string;
}
type ButtonPadProps = {
  radius?: number;
  names?: Array<string>;
  colors?: Array<string>;
  onClick?:(evt: ButtonPadEvent) => void;
};
export const ButtonPad = (props : ButtonPadProps)  =>  {
  const { onClick, radius = 45, names= ['X','Y','A','B'], colors = ["royalblue","limegreen","red","orange"] } = props;
  let textSize = radius;
  let btnStates= [false,false,false,false];
  const onButtonPress = (index:number) => {
    btnStates[index] = true;
    if(typeof(onClick)==="function"){
      onClick({
             states: btnStates,
             pressed: names[index]
         });
       }
  };
  
  const onButtonRel = (index:number) => {
    btnStates[index] = false;
    
    if(typeof(onClick)==="function"){
      onClick({
             states: btnStates,
             pressed: names[index]
         });
       }
  };
  
    return (
          <View style={{ flex: 1,justifyContent: "center", alignItems: "center",}}>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"green",
                  zIndex: 100,
                }}
              >
                <TouchableOpacity
                  style={[{
                    height: 2*radius,
                    width: 2*radius,
                    borderRadius: radius,
                    justifyContent: "center",
                    alignItems: "center"
                  }, {  top:"30%",backgroundColor: `${colors[0]}` }]} //top:"30%", right:"1%", 
                  //onPress={() => onButtonPress(0)}
                  onPressIn={() => onButtonPress(0)}
                  onPressOut={() => onButtonRel(0)}
                >
                  <Text style={{
                            fontSize: textSize,
                            color: "white",
                            fontWeight: "700"
                          }}>{names[0]}</Text>
                </TouchableOpacity>
              </View>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"gold",
                }}
              >
                <TouchableOpacity
                  style={[{
                    height: 2*radius,
                    width: 2*radius,
                    borderRadius: radius,
                    justifyContent: "center",
                    alignItems: "center"
                  }, { top: "50%", right:"20%", backgroundColor: `${colors[1]}` }]} //top:"2%", left:"10%",
                  //onPress={() => onButtonPress(1)}
                  onPressIn={() => onButtonPress(1)}
                  onPressOut={() => onButtonRel(1)}
                >
                  <Text style={{
                            fontSize: textSize,
                            color: "white",
                            fontWeight: "700"
                          }}>{names[1]}</Text>
                </TouchableOpacity>
              </View>
              <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"blue",
                }}
              >
                <TouchableOpacity
                  style={[{
                    height: 2*radius,
                    width: 2*radius,
                    borderRadius: radius,
                    justifyContent: "center",
                    alignItems: "center"
                  }, { bottom:"50%", left:"20%", backgroundColor: `${colors[2]}` }]} //bottom:"2%", 
                  //onPress={() => onButtonPress(2)}
                  onPressIn={() => onButtonPress(2)}
                  onPressOut={() => onButtonRel(2)}
                >
                  <Text style={{
                            fontSize: textSize,
                            color: "white",
                            fontWeight: "700"
                          }}>{names[2]}</Text>
                </TouchableOpacity>
              </View>
            <View
                style={{
                  //flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  //backgroundColor:"red",
                }}
              >
                <TouchableOpacity
                  style={[{
                    height: 2*radius,
                    width: 2*radius,
                    borderRadius: radius,
                    justifyContent: "center",
                    alignItems: "center"
                  }, { bottom:"30%", backgroundColor: `${colors[3]}` }]} //bottom:"30%", left:"1%",
                  //onPress={() => onButtonPress(3)}
                  onPressIn={() => onButtonPress(3)}
                  onPressOut={() => onButtonRel(3)}
                >
                  <Text style={{
                            fontSize: textSize,
                            color: "white",
                            fontWeight: "700"
                          }}>{names[3]}</Text>
                </TouchableOpacity>
              </View>
          </View>
    );
}
./App.tsx
import React from "react";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
import {ButtonPad} from "./src/ACJoysticks";
const GamePad = () =>  {
  const onPadClick = (data: any) => {
    console.log("from ButtonPad: ",data);
    //data.states[0], data.pressed
  }
    return (
      <View style={styles.mainBody}>
          <Text
            style={styles.mainTitle}>
            AC Controller
          </Text>
          <ButtonPad radius={60} names={[ '\u25B3', '\u25A2', '\u25EF',  '\u2573',]} colors={['black', 'black', 'black', 'black']} onClick={onPadClick} />
      </View>            
    );
}
export default GamePad;
//parameters
let BACKGROUND_COLOR = "#161616"; //191A19
let BUTTON_COLOR = "#346751"; //1E5128
let ERROR_COLOR = "#C84B31"; //4E9F3D
let TEXT_COLOR = "#ECDBBA"; //D8E9A8
const styles = StyleSheet.create({
  mainBody: {
    flex:1,
    justifyContent: 'center',
    alignItems: "center",
    color:TEXT_COLOR,
    backgroundColor: BACKGROUND_COLOR,
  },
  mainTitle:{
    color: TEXT_COLOR,
    fontSize: 30,
    textAlign: 'center',
    borderBottomWidth: 2,
    borderBottomColor: ERROR_COLOR,
    width:"100%"
  },
});
Aplicaciones
- Cree componentes reutilizables y configurables para sus aplicaciones
- Cree aplicaciones para controlar sus proyectos mediante Bluetooth, BLE o Wifi

