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