Crear proyecto
npx create-react-app miApp
Crear proyecto
npm start
Iniciar el proyecto en el navegador
Crear proyecto con Expo Go
npx create-expo-app miApp
Comando para crear proyecto
npm start
Generar el QR para iniciar el proyecto, el QR debe leerse desde la app Expo
Crear proyecto con React Native CLI
npx react-native init miApp
Crear proyecto
npx react-native start
Iniciar Metro para correr el proyecto
npx react-native run-android
Iniciar proyecto en Android
npx react-native run-ios
Iniciar proyecto en iOS
npx react-native log-ios
Acceder a los logs de iOS
npx react-native log-android
Acceder a los logs de Android
Comandos útiles
adb devices
Para ver los dispositivos conectados en donde podemos trabajar
xcrun simctl list devices
Lista los dispositivos iOS que podemos usar
Agregar ESLint
npm i eslint --save-dev
Instalar ESLint para ayudarnos con problemas de sintaxis
npm i @react-native-community/eslint-config --save-dev
Instalar ESLint de la comunidad
npm i --save-dev --save-exact prettier
Instalar Prettier para formatear el código, hay que crear .prettierrc.js y ahi agregar las reglas
Acceder al menú de debug
En un dispositivo físico se puede agitar el dispositivo para acceder al menú.
Para un emulador en iOS, es con command+d
Para un emulador en Android, es con command+m o control+m
Archivos
Para crear archivos, hay que agregar la extensión .tsx
index.tsx
Es el punto de entrada de la aplicación
tsconfig.json
Están las reglas de como queremos que trabaje TypeScript
Tipado
let variable: string;
Agregar una variable con un tipo string
let variable: string = ‘Ejemplo’;
Asignar un valor a la variable
let variable: string | number = 123;
La variable puede tomar un tipo string o un tipo number para su valor
Elementos
SafeAreaView
Contenedor que inicia la vista bajo la barra de estado
View
Contenedor que no puede ser scrolleado, equivalente al ‘div’ en HTML
Text
Contenedor de texto
StatusBar
Permite hacer modificaciones en el status bar, como cambiar el color de fondo, ocultarlo, estilo, etc.
ActivityIndicator
Permite poner el circulo de ‘cargando’ <ActivityIndicator color=“red” size={20}/>
FlatList
Permite crear listas
Estilos
Estilos en línea
<View style={{backgroundColor: ‘pink’}}>
Darle al View un background rosa
Estilos con condiciones
Para agregar un estilo u otro dependiendo de si una condición se cumple:
style={(condicion)
? styles.condicionVerdadera
: styles.condicionFalsa}
StyleSheet
Debe importarse StyleSheet de la API de React Native y crear un objeto antes de exportar el componente, se crea de la siguiente manera:
const styles = StyleSheet.create({
container: {
backgroundColor: ‘pink’
}
})
.
<View style={styles.container}>
Para agregarle los estilos a un elemento
<View style={[styles.container, styles.bgBlack]}>
Agregar varios estilos
Diseño
Box object model Se refiere al alto, ancho, margen, padding y borde
Position Puede ser absoluta, relativa, etc.
Flex box Habla de la dirección, ubicación, alineamiento, proporciones, etc.
Estilos en otro archivo
Podemos crear nuestro archivo de estilos:
import { StyleSheet } from "react-native";
const styles = StyleSheet.create({…});
export default styles;
Y usarlos normalmente en el archivo donde lo necesitemos, sin olvidar importarlo:
const App = () => {
return (
<SafeAreaView style={ styles.container }>…</SafeAreaView>
)
}
Interfaz
Sirven para poner reglas de validación a nuestros objetos.
interface Persona {
nombre: string,
edad: number,
direccion: {
pais: string,
estado: string;
}
}
La parte de ‘dirección’ puede extraerse en una nueva interfaz.
let persona1: Persona;
Se crea una variable que es del tipo ‘Persona’ de la interfaz creada.
Props
Son datos que llegan como parámetros en la función.
const App = (props: Props) => {…}
Si de antemano sabemos cuáles serán las propiedades que vamos a recibir, podemos desestructurarlo y elegir solo las propiedades que necesitamos:
const App = ({prop1, prop2}: Props) => {…}
Context y estado global de la App
Proporciona una forma de compartir datos entre componentes sin tener que pasar props a través de la jerarquía de componentes manualmente.
El context es como nuestro árbol de componentes.
Ejemplo básico
Aquí vamos a guardar la información del authState en la app para ser accedido por los hijos globalmente.
import { createContext } from "react";
// Define hoy will look like the general data
export interface AuthState {
isLoggedIn: boolean;
username?: string;
}
// Define which properties will be shared to the children, in case we don't want to
// add all of AuthState or we want to add more
export interface AuthContextProps {
authState: AuthState,
signIn: () => void;
}
// Initial state
export const authInitialState: AuthState = {
isLoggedIn: false,
username: undefined,
}
// Create context, it has the data that will be shared in all children components
export const AuthContext = createContext({} as AuthContextProps);
// State provider component
export const AuthProvider = ({children}: any) => {
const [authState, dispatch] = useReducer(authReducer, authInitialState)
const signIn = () => {
dispatch({type: 'signIn'})
}
return (
<AuthContext.Provider value={{
authState,
signIn
}}>
{children}
</AuthContext.Provider>
);
}
Podemos envolver App en una función que tenga el AuthProvider para que los elementos hijos tengan acceso a la información del AuthProvider:
const App = () => {
return (
<NavigationContainer>
<AppState>
<TabsNavigator />
</AppState>
</NavigationContainer>
)
}
const AppState = ({children}: any) => {
return (
<AuthProvider>
{children}
</AuthProvider>
)
}
Si queremos ejecutar un método de los definidos:
const {authState, signIn} = useContext(AuthContext);
<Text>
{JSON.stringify(authState, null, 4)}
</Text>
{authState.isLoggedIn && <Button title='Sign in' onPress={signIn} />}
Y hay que manejar el método en el authReducer, el payload funciona para mandar más información a la acción:
import { AuthState } from "./AuthContext";
type AuthAction =
{ type: 'signIn' } |
{ type: 'logout', payload: string };
// Generates a new state
export const authReducer = (state:AuthState, action: AuthAction):AuthState => {
switch (action.type) {
case 'signIn':
return {
...state,
isLoggedIn: true,
username: 'no-username'
}
default:
return state;
}
}
Imprimir los datos
Desde alguno de los componentes hijos podemos usar el hook de useContext:
const {authState} = useContext(AuthContext);
E imprimir su valor
<Text>{JSON.stringify(authState, null, 4)}</Text>
Funciones
const operacion = () => {…}
Declarar una función
const operacion = (): number => {…}
Declarar una función que retorna un dato de tipo number
const operacion = (a: string): number => {…}
Declarar una función que retorna un dato de tipo number y recibe un parámetro ‘a’ de tipo string
let resultado = operacion(“hola”);
Llamar a la función
Carpetas
Hay que crear una carpeta ‘src’ en la raíz del proyecto en donde hay que agregar el código/carpetas que necesitemos. Dentro de esa carpeta, podemos agregar las siguientes carpetas:
- API: Para almacenar lo referente a peticiones
- Assets: Guarda imágenes, videos, audios, etc.
- Components: Almacenar archivos necesarios para hooks, API, etc.
- Hooks: Aquí se guardan los hooks personalizados.
- Interfaces: Para archivos que muestran el tipado.
- Navigation: Contiene los archivos necesarios para la navegación. - Screens: Para almacenar las vistas del proyecto.
- Theme: Almacena los estilos globales y sus configuraciones.
Componentes
Podemos crear un archivo llamado Navbar.tsx y ahi crear la vista para un navbar, para utilizarlo en el archivo App.tsx hay que agregar su etiqueta e importar el archivo:
<Navbar />
A esos componentes podemos enviarles props:
<Navbar title=“Hola” />
Pero en el archivo Navbar.tsx tambien hay que definir cuales propiedades van a llegar:
const Navbar = (props: String) => {…}
o const Navbar = ({title}: String) => {…}
const Navbar = ({title?}: String) => {…}
Para agregar propiedades opcionales se agrega un ‘?’
const Navbar = ({title? = ‘Hola’}: String) => {…}
Se le da un valor por defecto a la propiedad, para en caso de que no reciba nada
Hooks
useState
const [valor, setValor] = useState(0);
Crear una variable de tipo number con valor inicial de 0
setValor(10);
Cambiamos el valor de ‘valor’ a 10
useReducer
const [state, dispatch] = useReducer[reducer, initialState, init];
Esta es la forma inicial que tiene un useReducer
Si queremos usar este hook para verificar si un usuario inició sesión, podemos agregar:
interface AuthState { token: string | null };
type AuthAction = { type: ‘logout’} | { type: ‘login’ };
const initialState:AuthState = {token: null};
const authReducer = (state: AuthState, action: AuthAction):AuthState => {
if(action.type == ‘logout’) {
return { token: null }
} else {
return { token: ’123abc’ }
}
}
export const Login = () => {
const [state, dispatch] = useReducer(authReducer, initialState];
useEffect(() => {
dispatch({type: ‘logout’});
}, []);
const login = () => {
dispatch({type: ‘login’});
}
const logout = () => {
dispatch({type: ‘logout’});
}
}
El initialState nos dice el estado inicial del valor.
El reducer siempre debe retornar un dato del mismo tipo del initialState, para lo que podemos crear una interfaz con los datos necesarios, además, recibe dos parámetros, un state para el valor del estado y un action que modifica el valor del state.
También tenemos un dispatch, el cual es el método disparador de la acción para cambiar el state, están envueltos en una función, esa función se la podemos agregar a un botón o un elemento donde lo necesitemos.
useEffect
Ejecuta el código en el momento en el que se carga el código en nuestra aplicación
useEffect(() => {
console.log(“Hola”);
}, []}
useNavigation
Sirve para recibir las props en la navegación desde un hook:
const navigator = useNavigation()
…
<Button title=‘Ir’ onPress={ () => navigator.navigate(‘Pagina’)} />
useWindowDimensions
const { width, height } = useWindowDimensions();
Permite obtener las dimensiones de la pantalla
useColorScheme
const colorScheme = useColorScheme();
Devuelve el tema que esta usando el dispositivo (light, dark…)
useSafeAreaInsets
Ayuda a trabajar como un SafeAreaView pero más personalizado. Por ejemplo aquí nos ayuda a agregar un margen superior del tamaño necesario para que se vea el contenido. Pero solo en los celulares donde sea necesario.
const insets = useSafeAreaInsets();
return (
<View style={.{ marginTop: insets.top }.}>
…
)
Hook personalizado
Para crear un archivo para almacenar un hook, hay que agregar ‘use’ al principio useHook.tsx
.
En ese archivo hay que agregar la lógica relacionada al hook, como crear las variables y los métodos, suponiendo que tenemos ‘valor’ y ‘setValor’ en nuestro nuevo hook, hay que retornarlo:
export const useHook = () => {
const [valor, setValor] = useState(0);
}
return { valor, setValor };
.
const { valor, setValor } = useHook();
Importar ese hook en otro archivo para usarlo
Recibir parámetros
export const useHook = (valorInicial: number = 10) => {
const [valor, setValor] = useState(0);
}
return { valor, setValor };
La función necesita un parámetro ‘valorInicial’ de tipo number el cual no es obligatorio, tomará el valor de 10 si no se le envía ningún parámetro
const { valor, setValor } = useHook(5);
Se le envía un valor como parámetro
Peticiones HTTP con Axios
npm i axios
Instalar axios
export const api = axios.create({ baseURL: ‘https://url.delapi/api});
Generar la conexión con la API para usarla más adelante
useEffect(() => {
api.get(‘/endpoint’)
.then(res => {
console.log(res.data);
})
.catch(console.error);
}, []);
Ejecutamos la llamada a la API en nuestro archivo, desde el useEffect e imprimimos la información necesaria
api.get<String>(‘/endpoint’)
Podemos decirle el tipo de dato que esperamos, en este caso, un dato de tipo string, o podemos crear una interfaz personalizada
Formularios
Podemos tener una variable para el formulario, con useState para guardar el valor cuando cambie.
La función onChange es la que se encarga de guardar el valor en la variable, heredando lo que ya se tenia, para no perder los datos.
const [formulario, setFormulario] = useState({
email: ‘ejemplo@test.com’,
password: ‘123’
});
const onChange = (value: string, campo:string) => {
setFormulario({
…formulario,
[campo]: value
});
}
…
<input
type=“text”
placeholder=“Email”
value={ formulario.email }
onChange = { ({ target }) => onChange(target.value, ‘email’) } />
Navegación
Stack Navigation
Funciona como una pila, en donde las pantallas se van poniendo una sobre otra, pero las pantallas siempre están activas.
Instalaciones
npm install @react-navigation/native
npm install react-native-screens react-native-safe-area-context
npm install @react-navigation/native-stack
En App
Hay que crear un NavigationContainer con un elemento Stack.Navigator, el cual es personalizado en este ejemplo:
const App = () => {
return (
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
)
}
Y en StackNavigator.tsx agregamos las pantallas que van a usarse
export const StackNavigator = () => {
return (
<Stack.Navigator>
<Stack.Screen name="BoxObjectMrodel" component={BoxObjectModel} />
<Stack.Screen name="Calculator" component={Calculator} />
<Stack.Screen name="Counter" component={Counter} />
<Stack.Screen name="Dimentions" component={Dimentions} />
<Stack.Screen name="HelloWorld" component={HelloWorld} />
<Stack.Screen name="Position" component={Position} />
</Stack.Navigator>
);
}
Agregar un botón para ir a otra pantalla
Hay que crear una interfaz para obtener las propiedades que pueden enviarse, además de tener las propiedades de la navegación, para poder navegar entre pantallas.
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
interface Props extends NativeStackScreenProps<any, any> {}
const MenuStackNavigator = ({navigation}: Props) => {
return (
<View>
<Button
title='Ir a’
onPress={() => {navigation.navigate(‘Pantalla’)}} />
</View>
)
}
Métodos de navigation
const MiPagina = ({navigation}:any) => { <Button onPress={() => navigation.*método*}}
Una vez que tenemos nuestro botón de ese modo, podemos agregar ciertos métodos:
navigation.pop()
Sirve para volver al elemento anterior en el Stack
navigation.popToTop()
Para ir al primer elemento del stack
Propiedades del Stack.Navigator
<Stack.Navigator *Propiedades*>
<Stack.Screen name="Menu" component={MenuStackNavigator} />
</Stack.Navigator>
.
initialRouteName=“Pagina2”
Define que pantalla será la primera en el stack
screenOptions={{cardStyle: {
headerShown: false
Mostrar el título de la pantalla en donde se encuentra o no
backgroundColor: ‘white’
Define el color que tendrá la vista (el contenido)
}, headerStyle: {
elevation: 0
Elimina la línea divisoria entre el título y el contenido (android)
shadowColor: ‘transparent’
Hace la línea divisoria de color transparente
}}}
Propiedades del Stack.Screen
<Stack.Navigator>
<Stack.Screen name="Menu" component={MenuStackNavigator} *Propiedades*/>
</Stack.Navigator>
.
options={{title:”Titulo 1”}}
Para cambiar el título de la página
Sobreescribir propiedades
Para sobreescribir el título del componente podemos usar un hook para agregar las propiedades necesarias:
useEffect(() => {
navigator.setOptions({
title: ‘Nuevo titulo’
headerBackTitle: ‘Atras’ //Para el texto iOS que aparece en la flecha de ir atrás
})
}, [])
Enviar argumentos entre pantallas
En el botón hay que agregar los argumentos después de poner a que pantalla queremos ir, puede ser una variable, un objeto, o algún elemento
<Button
title='Ir a’
onPress={() => {navigation.navigate(‘Pantalla’, argumentos)}} />
Para recibir los parámetros que enviamos podemos imprimirlo en consola:
console.log(JSON.stringify(props, null, 3));
Las props vienen en los parámetros de la función principal.
La mejor práctica para enviar parámetros se puede encontrar en la página del Navigator.
Drawer Navigation
Es la pantalla que se desliza desde un lado de la pantalla, como un menu lateral.
Instalaciones
npm install @react-navigation/drawer
Ejemplo básico
El drawer va a generar un menú lateral con los elementos que le agreguemos en Drawer.Screen, aparece al arrastrar el dedo de izquierda a derecha en la pantalla.
import { createDrawerNavigator } from '@react-navigation/drawer';
const Drawer = createDrawerNavigator();
function MyDrawer() {
return (
<Drawer.Navigator>
<Drawer.Screen name="Feed" component={Feed} />
<Drawer.Screen name="Article" component={Article} />
</Drawer.Navigator>
);
}
Personalizar el drawer
<Drawer.Navigator
drawerPosition=‘right’
El drawer aparece del lado derecho
drawerType=‘front’|’permanent’
Para elegir si queremos que por defecto el menú este oculto o no. Podemos usarlo para agregarlo dependiendo de si el celular esta en modo portrait o landscape agregando una condición ternaria: drawerType={condición}
.
drawerContent={(props) => <ContenidoInterno />}
Sirve para poder agregar contenido al drawer, como imágenes, vistas, etc. Hay que agregar los enlaces a otras secciones nuevamente con un TouchableOpacity/Button, etc
>
.
<Drawer.Screen options={{title’:Home’}} … />
Cambia el nombre que se verá en la pantalla
Agregar icono de hamburguesa
El ícono se coloca en las opciones de navegación y se agrega solo cuando inició la aplicación, por lo que podemos agregar un useEffect. Además hay que cambiar el tipo de las Props para poder tener acceso a las propiedades del drawer y de la navegación:
interface Props extends DrawerScreenProps<any, any>{};
…
useEffect(() => {
navigation.setOptions({
headerLeft: () => {
<Button title=“Menu” onPress{() => navigation.toggleDrawer()} />
}
})
})
BottomTab Navigation
Ofrece un menú inferior que funciona como una lista horizontal de enlaces que van hacia cada pantalla.
Instalaciones
npm install @react-navigation/bottom-tabs
Ejemplo básico
El Tab.Navigator contiene todas las pantallas que se van a colocar como Tabs
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
export const MyTabs = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
Personalizar las tabs
<Tab.Navigator
.
sceneContainerStyle={{background: ‘white’}}
Pone el color del contenido de la tab en blanco
screenOptions={({route}) => ({
tabBarActiveTintcolor: ‘red’,
Cambia el color del texto activo
tabBarStyle: {
.
borderTopColor: ‘red’,
Agrega un borde rojo a todo los tabs
borderTopWidth: 0,
El ancho del borde es 0
elevation: 0
Para que no se generen sombras (android)
},
.
tabBarLabelStyle: {fontSize: 14},
Cambia el tamaño de la fuente de las tabs
tabBarIcon: ({color, focused, size}) => {
Cambia el icono del tab
let iconName: string = '';
switch(route.name) {
case 'MenuStackNavigator':
iconName='MSN'
break;
Podemos usar un switch para ver que texto usar
return <Text style={{color}}>{iconName}</Text>
A todos los tabs les pone este texto como ícono y le pone color, el color que recibe como parámetro para que todos se vean igual
}})}
.
>
.
<TabScreen … options={{ tabBaricon: <Icono props />}}/>
Agregar un ícono, podemos llamar a una etiqueta que reciba datos para cambiar los estilos
Material Bottom Tab Navigation
Permite agregar estilos a la navegación que ya teníamos anteriormente en el menú inferior.
Instalación
npm install @react-navigation/material-bottom-tabs react-native-paper react-native-vector-icons
Ejemplo básico
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
const Tab = createMaterialBottomTabNavigator();
const MyTabs = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
Tenemos las mismas opciones de personalización que con los otros componentes.
Material Top Tab Navigation
Es un menú superior que funciona del mismo modo que los componentes anteriores, al permitirte navegar entre diferentes pantallas.
Instalaciones
npm install @react-navigation/material-top-tabs react-native-tab-view
npm install react-native-pager-view
Ejemplo básico
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
const Tab = createMaterialTopTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
Para personalizarlo tenemos las mismas opciones que con las otras navegaciones.
Ejecutar código dependiendo de la plataforma
Podemos crear una función para Android, otra para iOS dentro de nuestra función principal y validar en qué plataforma se está trabajando para saber qué código hay que retornar:
const app = () => {
const androidApp = () => {…}
const iosApp = () => {…}
return (Platform.OS === ‘ios’ ? iosApp() : androidApp();
}
Agregar iconos
Instalación
npm install --save react-native-vector-icons
Y seguir la guía de instalación en https://github.com/oblador/react-native-vector-icons
Hay que instalar los tipos del paquete de iconos que elijamos:
npm i -D @types/react-native-vector-icons
Agregar icono
import Icon from 'react-native-vector-icons/FontAwesome';
const myIcon = <Icon name="rocket" size={30} color="#900" />;
Animaciones
Creamos una función para que un elemento aparezca en pantalla, para eso necesitamos una referencia de su opacidad:
const opacity = useRef(new Animated.Value(0)).current;
Para hacer que un elemento aparezca, creamos la función:
const fadeIn = () => {
Animated.timing(
opacity,
{toValue: 1, duration: 1000, useNativeDriver: true}
).start();
}
Y agregamos el elemento Animated.View para lo que queremos animar, junto a un botón que inicie la animación:
<Animated.View style={{
backgroundColor: 'black',
width: 150,
height: 150,
borderColor: 'white',
borderWidth: 10,
opacity: opacity
}} />
<Button
title='Start fadeIn'
onPress={() => fadeIn()} />
Verificar el estado de la app
Con el AppState de react-native podemos monitorear cuando nuestra aplicación esta activa, inactiva o si está en segundo plano.
useEffect(() => {
AppState.addEventListener('change', state => {
console.log(state);
})
}, [])
Cambiar icono, nombre, splash screen de la app
Cambiar el nombre
Hay que ir a android/app/src/main/res/strings.xml y ahi actualizar el texto de la aplicación que viene por defecto
Cambiar ícono
Hay que crear un ícono (hay generadores de iconos en internet de donde podemos crear uno y descargarlo (descargar todas sus medidas disponibles).
Hay que tener images cuadradas y redondas. y almacenarlas en android/app/src/main/res/mipmap… cada una en su respectiva carpeta, y en cada una hay que tener una versión circular y una cuadrada del ícono.
En el AndroidManifest (android/app/src/main/AndroidManifest.xml) es en donde se definen los nombres de las imagenes que se toman por defecto.
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round”
Splash creen
Se puede seguir el tutorial de este enlace: https://github.com/crazycodeboy/react-native-splash-screen
Hacer las instalaciones, modificaciones de los archivos nativos, crear los archivos launch_screen.xml y generar el diseño para el splash creen, después importar el paquete en la pantalla donde lo queremos mostrar y configurar el splash screen.
Generar APK
Debe seguirse la guía de la documentación oficial: https://reactnative.dev/docs/signed-apk-android
## Subir a Google Play el app
Se sube desde: https://play.google.com/console/about/
Debe hacerse un pago y llenar los datos.
Otros
<Text style={styles.text}>Texto: {“\n”}Ejemplo</Text>
Para dar un salto de línea en el mismo Text
const { width, height } = Dimensions.get('window');
Obtener el ancho y alto de la pantalla pero al girar el dispositivo, las medidas se mantienen cuando deberían cambiar, es mejor usar el hook ‘useWindowDimentions’
<MiElemento opcionBool />
Si necesitamos enviarle props booleanas no es necesario inicializarla en true, el hecho de agregar la propiedad hace que se retorne un true
Links
React Native con TypeScript: https://create-react-app.dev/docs/adding-typescript/
React Navigation: https://reactnavigation.org/docs/getting-started/
React Drawer Navigation: https://reactnavigation.org/docs/drawer-based-navigation/
Bottom Tab Navigator: https://reactnavigation.org/docs/bottom-tab-navigator/
Material Top Tab Navigator: https://reactnavigation.org/docs/material-top-tab-navigator/
React Native APIs: https://reactnative.dev/docs/accessibilityinfo
Colores en iOS: https://developer.apple.com/design/human-interface-guidelines/color
Colores en Android: https://m2.material.io/design/color/color-usage.html
React Native con TypeScript: https://create-react-app.dev/docs/adding-typescript/
Iconos: https://github.com/oblador/react-native-vector-icons
Lista de iconos disponibles: https://ionic.io/ionicons
Carrusel: https://github.com/meliorence/react-native-snap-carousel
Gradientes: https://github.com/react-native-linear-gradient/react-native-linear-gradient
Analizar colores de imagenes: https://github.com/osamaqarem/react-native-image-colors
Temas: https://reactnavigation.org/docs/themes/
Permisos: https://www.npmjs.com/package/react-native-permissions
Extensiones: https://reactnative.dev/docs/app-extensions
Ejercicios: https://www.tutorialspoint.com/react_native/index.htm
Libro con ejercicios: https://books.goalkicker.com/ReactNativeBook/
Top comments (0)