Saltar al contenido principal

33 publicaciones etiquetadas con "engineering"

Ver todas las etiquetas

Preparando tu aplicación para iOS 15 y Android 12

· 4 min de lectura
Samuel Susla
Samuel Susla
Software Engineer @ Meta
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

¡Hola a todos!

Con las nuevas versiones de sistemas operativos móviles que se lanzarán a finales de este año, recomendamos preparar tus aplicaciones React Native con anticipación para evitar regresiones cuando estas versiones estén disponibles públicamente.

Presentación de los nuevos WebViews para iOS

· 3 min de lectura
Ingeniero de Software en Facebook
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Desde hace tiempo, Apple desaconseja el uso de UIWebViews en favor de WKWebView. En iOS 12, que se lanzará en los próximos meses, UIWebViews quedará oficialmente obsoleto. La implementación de WebView en React Native para iOS depende en gran medida de la clase UIWebView. Por lo tanto, ante estos cambios, hemos creado un nuevo backend nativo para iOS en el componente WebView de React Native que utiliza WKWebView.

La fase final de estos cambios se implementó en este commit y estará disponible en la versión 0.57.

Para utilizar esta nueva implementación, emplea la propiedad useWebKit:

<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>

Mejoras

UIWebView carecía de un método legítimo para facilitar la comunicación entre el JavaScript ejecutándose en el WebView y React Native. Cuando se enviaban mensajes desde el WebView, dependíamos de una solución alternativa para entregarlos a React Native. Brevemente: codificábamos los datos del mensaje en una URL con un esquema especial y navegábamos el WebView hacia ella. En el lado nativo, interceptábamos y cancelábamos esta navegación, extraíamos los datos de la URL y finalmente llamábamos a React Native. Esta implementación era propensa a errores e insegura. Me complace anunciar que hemos aprovechado las características de WKWebView para reemplazarla completamente.

Otras ventajas de WKWebView sobre UIWebView incluyen una ejecución de JavaScript más rápida y una arquitectura multiproceso. Consulta esta WWDC 2014 para más detalles.

Advertencias

Si tus componentes utilizan las siguientes propiedades, podrías experimentar problemas al cambiar a WKWebView. Por ahora, te sugerimos evitar usar estas props:

Comportamiento inconsistente:

automaticallyAdjustContentInsets y contentInsets (commit)

Cuando agregas contentInsets a un WKWebView, esto no cambia el viewport del WKWebView. El viewport mantiene el mismo tamaño que el frame. Con UIWebView, el tamaño del viewport sí cambia (se reduce si los content insets son positivos).

backgroundColor (commit)

Con la nueva implementación de WebView para iOS, existe la posibilidad de que el color de fondo parpadee si usas esta propiedad. Además, WKWebView renderiza fondos transparentes de manera diferente a UIWebview. Consulta la descripción del commit para más detalles.

No compatible:

scalesPageToFit (commit)

WKWebView no soportaba la propiedad scalesPageToFit, por lo que no pudimos implementarla en el componente WebView de React Native.

Actualizaciones de la API de accesibilidad

· 7 min de lectura
Ziqi Chen
Estudiante en UC Berkeley
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Motivación

A medida que la tecnología avanza y las aplicaciones móviles se vuelven cada vez más importantes en la vida cotidiana, la necesidad de crear aplicaciones accesibles también ha crecido en importancia.

La API de accesibilidad limitada de React Native siempre ha sido un gran punto de dolor para los desarrolladores, por lo que hemos realizado algunas actualizaciones en la API de accesibilidad para facilitar la creación de aplicaciones móviles inclusivas.

Problemas con la API existente

Problema uno: dos propiedades completamente diferentes pero similares - accessibilityComponentType (Android) y accessibilityTraits (iOS)

accessibilityComponentType y accessibilityTraits son dos propiedades que se utilizan para indicar a TalkBack en Android y VoiceOver en iOS con qué tipo de elemento de interfaz de usuario está interactuando el usuario. Los dos mayores problemas con estas propiedades son:

  1. Son dos propiedades diferentes con métodos de uso distintos, pero tienen el mismo propósito. En la API anterior, estas son dos propiedades separadas (una para cada plataforma), lo que no solo era inconveniente, sino también confuso para muchos desarrolladores. accessibilityTraits en iOS permite 17 valores diferentes, mientras que accessibilityComponentType en Android solo permite 4 valores. Además, los valores en su mayoría no tenían superposición. Incluso los tipos de entrada para estas dos propiedades son diferentes. accessibilityTraits permite pasar un array de rasgos o un solo rasgo, mientras que accessibilityComponentType solo permite un único valor.

  2. Existe una funcionalidad muy limitada en Android. Con la propiedad anterior, los únicos elementos de interfaz de usuario que Talkback podía reconocer eran "button", "radiobutton_checked" y "radiobutton_unchecked".

Problema dos: Pistas de accesibilidad inexistentes

Las pistas de accesibilidad ayudan a los usuarios que utilizan TalkBack o VoiceOver a comprender qué sucederá cuando realicen una acción en un elemento de accesibilidad que no es evidente solo por la etiqueta de accesibilidad. Estas pistas se pueden activar y desactivar en el panel de configuración. Anteriormente, la API de React Native no admitía pistas de accesibilidad en absoluto.

Problema tres: Ignorar colores invertidos

Algunos usuarios con pérdida de visión utilizan colores invertidos en sus teléfonos móviles para tener un mayor contraste en la pantalla. Apple proporcionó una API para iOS que permite a los desarrolladores ignorar ciertas vistas. De esta manera, las imágenes y los videos no se distorsionan cuando un usuario tiene activada la configuración de colores invertidos. Esta API actualmente no es compatible con React Native.

Diseño de la nueva API

Solución uno: Combinar accessibilityComponentType (Android) y accessibilityTraits (iOS)

Para resolver la confusión entre accessibilityComponentType y accessibilityTraits, decidimos fusionarlas en una sola propiedad. Esto tenía sentido porque técnicamente tenían la misma funcionalidad prevista y, al fusionarlas, los desarrolladores ya no tenían que preocuparse por las complejidades específicas de la plataforma al crear funciones de accesibilidad.

Antecedentes

En iOS, UIAccessibilityTraits es una propiedad que se puede establecer en cualquier NSObject. Cada uno de los 17 rasgos que se pasan a través de la propiedad de JavaScript a nativo se asigna a un elemento UIAccessibilityTraits en Objective-C. Cada rasgo está representado por un long int, y cada rasgo que se establece se combina mediante un OR.

Sin embargo, en Android, AccessibilityComponentType es un concepto inventado por React Native, y no se asigna directamente a ninguna propiedad en Android. La accesibilidad se maneja mediante un delegado de accesibilidad. Cada vista tiene un delegado de accesibilidad predeterminado. Si deseas personalizar cualquier acción de accesibilidad, tienes que crear un nuevo delegado de accesibilidad, sobrescribir los métodos específicos que deseas personalizar y luego establecer que el delegado de accesibilidad de la vista que estás manejando esté asociado con el nuevo delegado. Cuando un desarrollador establecía AccessibilityComponentType, el código nativo creaba un nuevo delegado basado en el componente que se pasaba, y establecía que la vista tuviera ese delegado de accesibilidad.

Cambios realizados

Para nuestra nueva propiedad, queríamos crear un superconjunto de ambas propiedades. Decidimos modelar la nueva propiedad principalmente siguiendo el comportamiento de la propiedad existente accessibilityTraits, ya que accessibilityTraits tiene significativamente más valores. La funcionalidad en Android para estos rasgos se implementaría mediante modificaciones en el Delegado de Accesibilidad.

Existen 17 valores de UIAccessibilityTraits que pueden asignarse a accessibilityTraits en iOS. Sin embargo, no incluimos todos como valores posibles en nuestra nueva propiedad. Esto se debe a que el efecto de configurar algunos de estos rasgos es poco conocido, y muchos de estos valores prácticamente no se utilizan.

Los valores de UIAccessibilityTraits generalmente cumplían uno de dos propósitos: describían el rol del elemento de UI o describían su estado. La mayoría de los usos observados combinaban un valor representando un rol con "state selected", "state disabled" o ambos. Por ello, decidimos crear dos nuevas propiedades: accessibilityRole y accessibilityState.

accessibilityRole

La nueva propiedad accessibilityRole indica a TalkBack o VoiceOver el rol de un elemento de UI. Puede tomar uno de estos valores:

  • none

  • button

  • link

  • search

  • image

  • keyboardkey

  • text

  • adjustable

  • header

  • summary

  • imagebutton

Esta propiedad solo permite un valor porque los elementos de UI generalmente no asumen más de un rol lógico. La excepción son imagen y botón, por lo que añadimos el rol imagebutton que combina ambos.

accessibilityStates

La nueva propiedad accessibilityStates indica a TalkBack o VoiceOver el estado de un elemento de UI. Acepta un array con uno o ambos valores:

  • selected

  • disabled

Solución dos: Adición de sugerencias de accesibilidad

Añadimos una nueva propiedad accessibilityHint. Configurarla permite a TalkBack o VoiceOver leer sugerencias a los usuarios.

accessibilityHint

Esta propiedad recibe la sugerencia de accesibilidad como cadena de texto.

En iOS, configura la propiedad nativa AccessibilityHint en la vista. VoiceOver leerá la sugerencia si están activadas en el iPhone.

En Android, el valor se añade al final de la etiqueta de accesibilidad. Esto imita el comportamiento de iOS, pero con la limitación de que en Android no pueden desactivarse mediante ajustes como en iOS.

Tomamos esta decisión porque las sugerencias suelen corresponder con acciones específicas (ej. clic) y queríamos mantener coherencia entre plataformas.

Solución al problema tres

accessibilityIgnoresInvertColors

Exponemos la API de Apple AccessibilityIgnoresInvertColors en JavaScript. Ahora puedes evitar inversión de colores en vistas (ej. imágenes) configurando esta propiedad como true.

Nuevo uso

Estas propiedades estarán disponibles en React Native 0.57.

Cómo actualizar

Si actualmente estás usando accessibilityComponentType y accessibilityTraits, estos son los pasos que puedes seguir para migrar a las nuevas propiedades.

1. Usando jscodeshift

Los casos de uso más simples pueden reemplazarse ejecutando un script de jscodeshift.

Este script reemplaza las siguientes instancias:

accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}

Con

accessibilityRole= “trait”

Este script también elimina instancias de AccessibilityComponentType (asumiendo que donde sea que configuraste AccessibilityComponentType, también configuraste AccessibilityTraits).

2. Usando un codemod manual

Para los casos que usaban AccessibilityTraits sin un valor correspondiente en AccessibilityRole, y los casos donde se pasaban múltiples rasgos en AccessibilityTraits, se requiere un codemod manual.

En general,

accessibilityTraits= {[“button”, “selected”]}

se reemplazaría manualmente con

accessibilityRole=“button”
accessibilityStates={[“selected”]}

Estas propiedades ya están siendo utilizadas en la base de código de Facebook. El codemod para Facebook fue sorprendentemente simple. El script de jscodeshift corrigió aproximadamente la mitad de nuestras instancias, y la otra mitad se corrigió manualmente. En general, todo el proceso tomó menos de unas pocas horas.

¡Esperamos que encuentres útil la API actualizada! Y por favor, ¡sigan haciendo aplicaciones accesibles! #inclusion

Estado de React Native 2018

· 5 min de lectura
Sophie Alpert
Gerente de Ingeniería en React en Facebook
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Ha pasado tiempo desde nuestra última actualización sobre el estado de React Native.

En Facebook, usamos React Native más que nunca para proyectos importantes. Marketplace, una de nuestras soluciones más populares, es una pestaña principal de nuestra aplicación mensual usada por 800 millones de personas. Desde su creación en 2015, todo Marketplace se ha construido con React Native, incluyendo más de cien vistas de pantalla completa en distintas secciones de la app.

También empleamos React Native en nuevas áreas de la aplicación. Quienes vieron el evento F8 del mes pasado reconocerán Donaciones de Sangre, Respuesta ante Crisis, Accesos Directos de Privacidad y Chequeos de Bienestar: todas funciones recientes desarrolladas con React Native. Los proyectos externos a la app principal de Facebook también usan React Native. El nuevo visor VR Oculus Go incluye una app móvil complementaria totalmente construida con React Native, sin mencionar que React VR potencia experiencias dentro del propio dispositivo.

Naturalmente, también utilizamos otras tecnologías para nuestras apps. Litho y ComponentKit son bibliotecas que empleamos extensivamente; ambas ofrecen una API de componentes similar a React para construir pantallas nativas. Nunca fue objetivo de React Native reemplazar otras tecnologías: nos enfocamos en mejorarlo, pero nos complace ver equipos adoptar conceptos como la recarga instantánea para código no JavaScript.

Arquitectura

Al iniciar React Native en 2013, diseñamos un único "puente" entre JavaScript y nativo que fuera asíncrono, serializable y por lotes. Así como React DOM convierte actualizaciones de estado en llamadas imperativas a APIs del DOM como document.createElement(attrs) y .appendChild(), React Native devuelve un mensaje JSON con mutaciones a ejecutar, tipo [["createView", attrs], ["manageChildren", ...]]. Diseñamos el sistema para nunca depender de respuestas síncronas y garantizar que todo fuera serializable a JSON. Esto nos dio flexibilidad: sobre esta arquitectura construimos herramientas como la depuración en Chrome, que ejecuta código JavaScript asíncronamente vía WebSocket.

Tras 5 años, descubrimos que estos principios iniciales dificultan ciertas funcionalidades. Un puente asíncrono impide integrar lógica JavaScript con APIs nativas que requieren respuestas síncronas. Un puente por lotes encola llamadas nativas, complicando la invocación de funciones nativas desde apps React Native. Y un puente serializable implica copiar datos innecesariamente en lugar de compartir memoria directamente. Para apps completamente en React Native, estas limitaciones son manejables. Pero en apps con integración compleja entre código nativo y React Native, resultan frustrantes.

Estamos reestructurando React Native a gran escala para flexibilizar el framework y mejorar su integración con infraestructura nativa en apps híbridas JavaScript/nativas. Con este proyecto aplicaremos aprendizajes de los últimos 5 años y modernizaremos gradualmente nuestra arquitectura. Reescribimos internos de React Native, pero la mayoría de cambios son internos: las apps existentes seguirán funcionando con pocas o ninguna modificación.

Para hacer React Native más ligero y que se integre mejor en aplicaciones nativas existentes, esta reestructuración incluye tres cambios internos importantes. Primero, estamos modificando el modelo de subprocesos. En lugar de requerir que cada actualización de UI realice trabajo en tres hilos diferentes, será posible llamar sincrónicamente a JavaScript en cualquier hilo para actualizaciones de alta prioridad, manteniendo el trabajo de baja prioridad fuera del hilo principal para preservar la capacidad de respuesta. Segundo, incorporaremos capacidades de renderizado asíncrono en React Native para permitir múltiples prioridades de renderizado y simplificar el manejo de datos asíncronos. Finalmente, simplificaremos nuestro puente para hacerlo más rápido y liviano; las llamadas directas entre nativo y JavaScript serán más eficientes y facilitarán la creación de herramientas de depuración como trazas de pila entre lenguajes.

Una vez completados estos cambios, serán posibles integraciones más estrechas. Actualmente, es imposible incorporar navegación nativa y manejo de gestos, o componentes nativos como UICollectionView y RecyclerView sin soluciones complejas. Tras nuestras modificaciones al modelo de subprocesos, desarrollar estas características será directo.

Publicaremos más detalles sobre este trabajo más adelante este año, conforme se acerque su finalización.

Comunidad

Junto a la comunidad interna de Facebook, nos complace contar con una próspera comunidad de usuarios y colaboradores de React Native fuera de Facebook. Queremos apoyar más a esta comunidad, tanto mejorando la experiencia para los usuarios como facilitando las contribuciones al proyecto.

Así como nuestros cambios arquitectónicos ayudarán a React Native a interoperar mejor con otras infraestructuras nativas, React Native debería ser más delgado en el lado de JavaScript para integrarse mejor con su ecosistema, lo que incluye hacer intercambiables la VM y el empaquetador. Reconocemos que el ritmo de los cambios disruptivos puede ser difícil de seguir, por lo que buscamos formas de reducir las versiones mayores. Finalmente, sabemos que algunos equipos necesitan documentación más exhaustiva en temas como optimización de arranque, donde aún no hemos plasmado nuestra experiencia. Esperen ver estos cambios durante el próximo año.

Si usas React Native, eres parte de nuestra comunidad; sigue compartiendo cómo podemos mejorarlo para ti.

React Native es solo una herramienta en el arsenal de un desarrollador móvil, pero es una en la que creemos firmemente. La estamos mejorando cada día, con más de 2500 commits en el último año provenientes de más de 500 colaboradores.

Usar TypeScript con React Native

· 8 min de lectura
Ash Furrow
Ingeniero de Software en Artsy
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

¡JavaScript! Todos lo amamos. Pero algunos también adoramos los tipos de datos. Afortunadamente, existen opciones para añadir tipos más fuertes a JavaScript. Mi favorito es TypeScript, aunque React Native incluye soporte para Flow de forma nativa. Tu preferencia dependerá de gustos personales, ya que cada uno tiene su propio enfoque para añadir la magia de los tipos a JavaScript. Hoy veremos cómo usar TypeScript en aplicaciones de React Native.

Esta publicación utiliza como guía el repositorio TypeScript-React-Native-Starter de Microsoft.

Actualización: Desde que se escribió esta entrada, el proceso se ha simplificado aún más. Puedes reemplazar toda la configuración descrita en este post ejecutando un solo comando:

npx react-native init MyAwesomeProject --template react-native-template-typescript

Sin embargo, existen algunas limitaciones en el soporte de TypeScript en Babel, que el artículo anterior explica en detalle. Los pasos descritos en esta publicación siguen funcionando, y Artsy aún utiliza react-native-typescript-transformer en producción, pero la forma más rápida de comenzar con React Native y TypeScript es usando el comando anterior. Siempre puedes cambiar más adelante si es necesario.

En cualquier caso, ¡disfrútalo! La publicación original continúa a continuación.

Prerrequisitos

Dado que podrías estar desarrollando en varias plataformas diferentes dirigidas a distintos tipos de dispositivos, la configuración básica puede ser compleja. Primero debes asegurarte de poder ejecutar una aplicación React Native básica sin TypeScript. Sigue las instrucciones en el sitio web de React Native para comenzar. Cuando hayas logrado implementar en un dispositivo o emulador, estarás listo para iniciar una aplicación React Native con TypeScript.

También necesitarás Node.js, npm y Yarn.

Inicialización

Una vez que hayas probado a crear un proyecto ordinario de React Native, estarás listo para añadir TypeScript. Vamos a hacerlo.

react-native init MyAwesomeProject
cd MyAwesomeProject

Agregar TypeScript

El siguiente paso es añadir TypeScript a tu proyecto. Los siguientes comandos:

  • añadirán TypeScript a tu proyecto

  • agregarán React Native TypeScript Transformer a tu proyecto

  • inicializarán un archivo de configuración vacío de TypeScript, que configuraremos después

  • añadirán un archivo de configuración vacío para React Native TypeScript Transformer, que configuraremos a continuación

  • agregarán tipados para React y React Native

Bien, ejecutemos estos comandos.

yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native

El archivo tsconfig.json contiene todas las configuraciones del compilador de TypeScript. Los valores predeterminados creados por el comando anterior son adecuados en su mayoría, pero abre el archivo y descomenta la siguiente línea:

{
/* Search the config file for the following line and uncomment it. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
}

El archivo rn-cli.config.js contiene la configuración de React Native TypeScript Transformer. Ábrelo y añade lo siguiente:

module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};

Migrar a TypeScript

Renombra los archivos generados App.js y __tests_/App.js como App.tsx. index.js debe mantener la extensión .js. Todos los archivos nuevos deben usar la extensión .tsx (o .ts si el archivo no contiene JSX).

Si intentaras ejecutar la aplicación ahora, obtendrías un error como object prototype may only be an object or null. Esto se debe a un fallo al importar la exportación predeterminada de React junto con una exportación nombrada en la misma línea. Abre App.tsx y modifica la importación al inicio del archivo:

-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';

Parte de esto se relaciona con diferencias en cómo Babel y TypeScript interoperan con módulos CommonJS. En el futuro, ambos convergerán en el mismo comportamiento.

En este punto, deberías poder ejecutar la aplicación de React Native.

Configuración de pruebas con TypeScript

React Native incluye Jest por defecto. Para probar aplicaciones React Native con TypeScript, añadiremos ts-jest a nuestras devDependencies.

yarn add --dev ts-jest

Luego, abriremos nuestro package.json y reemplazaremos el campo jest con lo siguiente:

{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}

Esto configurará Jest para ejecutar archivos .ts y .tsx con ts-jest.

Instalación de declaraciones de tipos para dependencias

Para obtener la mejor experiencia en TypeScript, necesitamos que el verificador de tipos comprenda la estructura y API de nuestras dependencias. Algunas bibliotecas publican paquetes con archivos .d.ts (archivos de declaración de tipos), que describen la estructura del JavaScript subyacente. Para otras bibliotecas, tendremos que instalar explícitamente el paquete correspondiente en el ámbito npm @types/.

Por ejemplo, aquí necesitaremos tipos para Jest, React, React Native y React Test Renderer.

yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer

Guardamos estos paquetes de declaración de tipos como dependencias de desarrollo porque esta es una aplicación React Native que solo usa estas dependencias durante el desarrollo, no en tiempo de ejecución. Si estuviéramos publicando una biblioteca en NPM, podríamos tener que añadir algunas de estas dependencias de tipos como dependencias regulares.

Puedes leer más aquí sobre cómo obtener archivos .d.ts.

Ignorar más archivos

Para tu control de código fuente, querrás ignorar la carpeta .jest. Si usas git, podemos simplemente añadir entradas a nuestro archivo .gitignore.

# Jest
#
.jest/

Como punto de control, considera confirmar tus archivos en el sistema de control de versiones.

git init
git add .gitignore # import to do this first, to ignore our files
git add .
git commit -am "Initial commit."

Añadiendo un componente

Añadamos un componente a nuestra aplicación. Creemos un componente Hello.tsx. Es un componente pedagógico, no algo que escribirías realmente en una app, pero algo no trivial que muestra cómo usar TypeScript en React Native.

Crea un directorio components y añade el siguiente ejemplo.

// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';

export interface Props {
name: string;
enthusiasmLevel?: number;
}

interface State {
enthusiasmLevel: number;
}

export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'You could be a little more enthusiastic. :D',
);
}

this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}

onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');

render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>

<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="decrement"
color="red"
/>
</View>

<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="increment"
color="blue"
/>
</View>
</View>
</View>
);
}
}

// styles
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});

¡Vaya! Es mucho, pero analicémoslo:

  • En lugar de renderizar elementos HTML como div, span, h1, etc., renderizamos componentes como View y Button. Estos son componentes nativos que funcionan en diferentes plataformas.

  • Los estilos se especifican usando la función StyleSheet.create que nos proporciona React Native. Las hojas de estilo de React nos permiten controlar el diseño usando Flexbox y estilizar usando construcciones similares a CSS.

Añadiendo pruebas para el componente

Ahora que tenemos un componente, probémoslo.

Ya tenemos Jest instalado como ejecutor de pruebas. Escribiremos pruebas de snapshot para nuestros componentes. Añadamos el complemento necesario para estas pruebas:

yarn add --dev react-addons-test-utils

Ahora creemos una carpeta __tests__ dentro del directorio components y añadamos una prueba para Hello.tsx:

// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';

import {Hello} from '../Hello';

it('renders correctly with defaults', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});

La primera vez que se ejecuta la prueba, creará una instantánea del componente renderizado y la almacenará en el archivo components/__tests__/__snapshots__/Hello.tsx.snap. Cuando modifiques tu componente, deberás actualizar las instantáneas y revisar los cambios inadvertidos. Puedes obtener más información sobre cómo probar componentes de React Native aquí.

Próximos pasos

Consulta el tutorial oficial de React y la biblioteca de gestión del estado Redux. Estos recursos pueden ser útiles al desarrollar aplicaciones con React Native. Además, quizás quieras explorar ReactXP, una biblioteca de componentes escrita completamente en TypeScript que soporta tanto React para web como React Native.

¡Disfruta de un entorno de desarrollo React Native más seguro con tipos!

Construyendo <InputAccessoryView> para React Native

· 7 min de lectura
Peter Argany
Ingeniero de Software en Facebook
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Motivación

Hace tres años, se abrió un issue en GitHub para solicitar compatibilidad con input accessory view en React Native.

Durante los años siguientes, hubo innumerables '+1', diversas soluciones alternativas y cero cambios concretos en RN sobre este tema, hasta hoy. Comenzando con iOS, exponemos una API para acceder a la vista de accesorios nativa y nos emociona compartir cómo la construimos.

Antecedentes

¿Qué es exactamente una input accessory view? Según la documentación para desarrolladores de Apple, es una vista personalizable que puede anclarse en la parte superior del teclado del sistema cuando un receptor se convierte en primer respondedor. Cualquier elemento que herede de UIResponder puede redeclarar la propiedad .inputAccessoryView como lectura-escritura y gestionar una vista personalizada aquí. La infraestructura de respondedores monta la vista y la mantiene sincronizada con el teclado del sistema. Los gestos que ocultan el teclado (como arrastrar o tocar) se aplican a la vista de accesorios a nivel de framework. Esto permite crear contenido con ocultamiento interactivo del teclado, una característica fundamental en aplicaciones de mensajería de primer nivel como iMessage y WhatsApp.

Existen dos casos de uso comunes para anclar una vista al teclado. El primero es crear una barra de herramientas, como el selector de fondos en el compositor de Facebook.

En este escenario, el teclado está enfocado en un campo de texto, y la vista de accesorios proporciona funcionalidad adicional contextual. En aplicaciones de mapas podría mostrar sugerencias de direcciones, mientras que en editores de texto podría incluir herramientas de formato avanzado.


El UIResponder de Objective-C que posee el <InputAccessoryView> en este caso es claro: el <TextInput> se convierte en primer respondedor, y en el código nativo esto se traduce en una instancia de UITextView o UITextField.

El segundo escenario común son entradas de texto persistentes:

Aquí, la entrada de texto forma parte de la propia vista de accesorios. Es común en aplicaciones de mensajería, donde puedes componer mensajes mientras navegas por un hilo de conversación.


¿Quién posee el <InputAccessoryView> aquí? ¿Puede ser nuevamente UITextView o UITextField? La entrada de texto está dentro de la vista de accesorios, lo que sugiere dependencia circular. Resolver esto merece su propia publicación. Spoiler: el propietario es una subclase genérica de UIView que invocamos manualmente con becomeFirstResponder.

Diseño de API

Ahora que sabemos qué es <InputAccessoryView> y cómo usarlo, el siguiente paso es diseñar una API coherente para ambos casos de uso, compatible con componentes existentes como <TextInput>.

Para barras de herramientas del teclado, debemos considerar:

  1. Poder integrar cualquier jerarquía de vistas de React Native en el <InputAccessoryView>.

  2. Que esta jerarquía de vistas genérica y desacoplada acepte interacciones táctiles y pueda manipular el estado de la aplicación.

  3. Vincular un <InputAccessoryView> a un <TextInput> específico.

  4. Compartir un <InputAccessoryView> entre múltiples entradas de texto sin duplicar código.

Podemos lograr el punto #1 usando un concepto similar a los portales de React. En este diseño, trasladamos las vistas de React Native a una jerarquía UIView gestionada por la infraestructura de respuesta. Dado que las vistas de React Native se renderizan como UIViews, esto es bastante directo: simplemente podemos sobrescribir:

- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex

y redirigir todas las subvistas a una nueva jerarquía UIView. Para el punto #2, configuramos un nuevo RCTTouchHandler para el <InputAccessoryView>. Las actualizaciones de estado se logran mediante callbacks de eventos regulares. Para los puntos #3 y #4, usamos el campo nativeID para localizar la jerarquía UIView de la vista accesoria en código nativo durante la creación de un componente <TextInput>. Esta función utiliza la propiedad .inputAccessoryView del campo de texto nativo subyacente. Esto enlaza efectivamente <InputAccessoryView> con <TextInput> en sus implementaciones de ObjC.

Soportar campos de texto persistentes (escenario 2) añade más restricciones. En este diseño, la vista accesoria tiene un campo de texto como hijo, por lo que vincular mediante nativeID no es viable. En su lugar, establecemos la propiedad .inputAccessoryView de una UIView genérica fuera de pantalla a nuestra jerarquía nativa <InputAccessoryView>. Al indicar manualmente a esta UIView genérica que se convierta en primera respondedora, la jerarquía se monta mediante la infraestructura de respuesta. Este concepto se explica detalladamente en la publicación de blog mencionada anteriormente.

Dificultades

Por supuesto, no todo fue sencillo al construir esta API. Estas son algunas dificultades que encontramos y cómo las solucionamos.

Una idea inicial para construir esta API implicaba escuchar a NSNotificationCenter los eventos UIKeyboardWill(Show/Hide/ChangeFrame). Este patrón se usa en bibliotecas de código abierto y en partes internas de la app de Facebook. Desafortunadamente, los eventos UIKeyboardDidChangeFrame no se activaban a tiempo para actualizar el marco de <InputAccessoryView> durante deslizamientos. Además, estos eventos no capturan cambios en la altura del teclado. Esto genera errores que se manifiestan así:

En iPhone X, los teclados de texto y emoji tienen alturas diferentes. Muchas apps que usan eventos de teclado para manipular campos de texto tuvieron que corregir este error. Nuestra solución fue comprometernos a usar la propiedad .inputAccessoryView, haciendo que la infraestructura de respuesta gestione estas actualizaciones de marco.


Otro error complejo fue evitar la barra de inicio en iPhone X. Podrías pensar: "¡Apple creó safeAreaLayoutGuide para esto, es trivial!". Fuimos igual de ingenuos. El primer problema es que la implementación nativa de <InputAccessoryView> no tiene ventana para anclarse hasta que está por aparecer. Podemos sobrescribir -(BOOL)becomeFirstResponder y aplicar restricciones de diseño allí. Al cumplir estas restricciones, la vista accesoria sube, pero aparece otro error:

La vista accesoria evita la barra de inicio, pero ahora se ve contenido tras el área insegura. La solución está en este radar. Envolví la jerarquía nativa de <InputAccessoryView> en un contenedor que no cumple las restricciones de safeAreaLayoutGuide. El contenedor nativo cubre el contenido del área insegura, mientras el <InputAccessoryView> permanece dentro de los límites seguros.


Ejemplo de Uso

Este ejemplo crea un botón en la barra de herramientas del teclado para resetear el estado de <TextInput>:

class TextInputAccessoryViewExample extends React.Component<
{},
*,
> {
constructor(props) {
super(props);
this.state = {text: 'Placeholder Text'};
}

render() {
const inputAccessoryViewID = 'inputAccessoryView1';
return (
<View>
<TextInput
style={styles.default}
inputAccessoryViewID={inputAccessoryViewID}
onChangeText={text => this.setState({text})}
value={this.state.text}
/>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<View style={{backgroundColor: 'white'}}>
<Button
onPress={() =>
this.setState({text: 'Placeholder Text'})
}
title="Reset Text"
/>
</View>
</InputAccessoryView>
</View>
);
}
}

Otro ejemplo de Campos de Texto Persistentes está disponible en el repositorio.

¿Cuándo podré usar esto?

El commit completo para la implementación de esta característica está aquí. <InputAccessoryView> estará disponible en la próxima versión v0.55.0.

¡Feliz escritura con teclado :)

Uso de AWS con React Native

· 11 min de lectura
Richard Threlkeld
Gerente Senior de Producto Técnico en AWS Mobile
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

AWS es ampliamente reconocido en la industria tecnológica como proveedor de servicios en la nube. Estos incluyen tecnologías de computación, almacenamiento y bases de datos, así como ofertas serverless completamente gestionadas. El equipo de AWS Mobile ha estado colaborando estrechamente con clientes y miembros del ecosistema JavaScript para que las aplicaciones móviles y web conectadas a la nube sean más seguras, escalables y fáciles de desarrollar e implementar. Comenzamos con un kit de inicio completo, pero tenemos desarrollos más recientes.

Esta publicación de blog trata sobre aspectos interesantes para desarrolladores de React y React Native:

  • AWS Amplify, una biblioteca declarativa para aplicaciones JavaScript que utiliza servicios en la nube

  • AWS AppSync, un servicio GraphQL completamente gestionado con funciones offline y en tiempo real

AWS Amplify

Las aplicaciones de React Native son muy fáciles de iniciar usando herramientas como Create React Native App y Expo. Sin embargo, conectarlas a la nube puede ser un desafío cuando intentas alinear un caso de uso con servicios de infraestructura. Por ejemplo, tu aplicación de React Native podría necesitar subir fotos. ¿Deberían protegerse por usuario? Eso probablemente signifique que necesitas algún tipo de registro o proceso de inicio de sesión. ¿Quieres tu propio directorio de usuarios o estás usando un proveedor de redes sociales? Tal vez tu aplicación también necesite llamar a una API con lógica de negocio personalizada después de que los usuarios inicien sesión.

Para ayudar a los desarrolladores JavaScript con estos problemas, lanzamos una biblioteca llamada AWS Amplify. Su diseño se organiza en "categorías" de tareas, en lugar de implementaciones específicas de AWS. Por ejemplo, si quisieras que los usuarios se registren, inicien sesión y luego suban fotos privadas, simplemente incorporarías las categorías Auth y Storage a tu aplicación:

import { Auth } from 'aws-amplify';

Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err));

Auth.confirmSignIn(user, code)
.then(data => console.log(data))
.catch(err => console.log(err));

En el código anterior, puedes ver un ejemplo de tareas comunes que Amplify te ayuda a realizar, como usar códigos de autenticación multifactor (MFA) con correo electrónico o SMS. Las categorías admitidas actualmente son:

  • Auth: Proporciona automatización de credenciales. La implementación lista para usar emplea credenciales de AWS para firmas y tokens JWT OIDC de Amazon Cognito. Se admite funcionalidad común, como características MFA.

  • Analytics: Con una sola línea de código, obtén seguimiento para usuarios autenticados o no autenticados en Amazon Pinpoint. Extiéndelo para métricas o atributos personalizados según prefieras.

  • API: Proporciona interacción con APIs RESTful de manera segura, aprovechando AWS Signature Version 4. El módulo API funciona excelente en infraestructuras serverless con Amazon API Gateway.

  • Storage: Comandos simplificados para subir, descargar y listar contenido en Amazon S3. También puedes agrupar datos fácilmente en contenido público o privado por usuario.

  • Caching: Interfaz de caché LRU para aplicaciones web y React Native que utiliza persistencia específica de implementación.

  • i18n and Logging: Proporciona capacidades de internacionalización y localización, así como capacidades de depuración y registro.

Una ventaja de Amplify es que codifica "mejores prácticas" en el diseño para tu entorno de programación específico. Por ejemplo, descubrimos al trabajar con clientes y desarrolladores de React Native que los atajos tomados durante el desarrollo para hacer funcionar las cosas rápidamente llegaban a entornos de producción. Estos pueden comprometer la escalabilidad o la seguridad, y forzar una reestructuración de infraestructura y refactorización de código.

Un ejemplo de cómo ayudamos a los desarrolladores a evitar esto son las Arquitecturas de Referencia Serverless con AWS Lambda. Estas muestran las mejores prácticas para usar Amazon API Gateway y AWS Lambda juntos al construir tu backend. Este patrón está integrado en la categoría API de Amplify. Puedes usar este patrón para interactuar con varios endpoints REST diferentes y pasar cabeceras directamente a tu función Lambda para lógica de negocio personalizada. También hemos lanzado una CLI de AWS Mobile para iniciar nuevos proyectos de React Native o integrarlo en existentes con estas características. Para comenzar, simplemente instala mediante npm y sigue las indicaciones de configuración:

npm install --global awsmobile-cli
awsmobile configure

Otro ejemplo de mejores prácticas integradas específicas para el ecosistema móvil es la seguridad de contraseñas. La implementación predeterminada de la categoría Auth aprovecha los grupos de usuarios de Amazon Cognito para registro e inicio de sesión. Este servicio implementa el protocolo Secure Remote Password para proteger a los usuarios durante los intentos de autenticación. Si te interesa leer sobre las matemáticas del protocolo, notarás que debes usar un número primo grande al calcular el verificador de contraseña sobre una raíz primitiva para generar un Grupo. En entornos React Native, JIT está deshabilitado. Esto hace que los cálculos BigInteger para operaciones de seguridad como esta sean menos eficientes. Para solucionarlo, hemos lanzado puentes nativos en Android e iOS que puedes vincular en tu proyecto:

npm install --save aws-amplify-react-native
react-native link amazon-cognito-identity-js

También nos emociona ver que el equipo de Expo ha incluido esto en su último SDK para que puedas usar Amplify sin necesidad de ejectar.

Finalmente, específico para desarrollo en React Native (y React), Amplify incluye componentes de orden superior (HOCs) para envolver fácilmente funcionalidades, como registro e inicio de sesión en tu aplicación:

import Amplify, { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

class App extends React.Component {
...
}

export default withAuthenticator(App);

El componente subyacente también se proporciona como <Authenticator />, que te da control total para personalizar la UI. También ofrece propiedades para gestionar el estado del usuario, como si ha iniciado sesión o está esperando confirmación MFA, y callbacks que puedes activar cuando cambia el estado.

De manera similar, encontrarás componentes React genéricos para diferentes casos de uso. Puedes personalizarlos según tus necesidades, por ejemplo, para mostrar todas las imágenes privadas de Amazon S3 en el módulo Storage:

<S3Album
level="private"
path={path}
filter={(item) => /jpg/i.test(item.path)}/>

Puedes controlar muchas características de los componentes mediante props, como se mostró anteriormente con opciones de almacenamiento público o privado. Incluso hay capacidades para recopilar automáticamente análisis cuando los usuarios interactúan con ciertos componentes de UI:

return <S3Album track/>

AWS Amplify favorece un estilo de desarrollo de convención sobre configuración, con rutinas de inicialización global o por categoría. La forma más rápida de comenzar es con un archivo aws-exports. Sin embargo, los desarrolladores también pueden usar la biblioteca independientemente con recursos existentes.

Para un análisis profundo de la filosofía y ver una demo completa, mira el video de AWS re:Invent.

AWS AppSync

Poco después del lanzamiento de AWS Amplify, también lanzamos AWS AppSync. Este es un servicio GraphQL completamente administrado con capacidades tanto fuera de línea como en tiempo real. Aunque puedes usar GraphQL en diferentes lenguajes de programación cliente (incluyendo Android e iOS nativos), es muy popular entre los desarrolladores de React Native. Esto se debe a que el modelo de datos encaja perfectamente en un flujo de datos unidireccional y jerarquía de componentes.

AWS AppSync te permite conectar con recursos en tu propia cuenta de AWS, lo que significa que eres dueño y controlas tus datos. Esto se logra mediante fuentes de datos, y el servicio admite Amazon DynamoDB, Amazon Elasticsearch y AWS Lambda. Esto te permite combinar funcionalidades (como bases de datos NoSQL y búsqueda de texto completo) en una única API GraphQL como esquema. AppSync también puede aprovisionar recursos desde un esquema, así que si no estás familiarizado con los servicios de AWS, puedes escribir SDL de GraphQL, hacer clic en un botón y comenzar a operar automáticamente.

La funcionalidad en tiempo real de AWS AppSync se controla mediante suscripciones GraphQL con un patrón basado en eventos bien conocido. Como las suscripciones en AWS AppSync se controlan en el esquema con una directiva GraphQL, y un esquema puede usar cualquier fuente de datos, esto significa que puedes activar notificaciones desde operaciones de bases de datos con Amazon DynamoDB y Amazon Elasticsearch Service, o desde otras partes de tu infraestructura con AWS Lambda.

De manera similar a AWS Amplify, puedes usar funciones de seguridad empresarial en tu API GraphQL con AWS AppSync. El servicio te permite comenzar rápidamente con claves de API. Sin embargo, al pasar a producción puedes migrar a AWS Identity and Access Management (IAM) o tokens OIDC de grupos de usuarios de Amazon Cognito. Puedes controlar el acceso a nivel de resolutor con políticas en tipos. Incluso puedes usar verificaciones lógicas para control de acceso granular en tiempo de ejecución, como detectar si un usuario es propietario de un recurso específico de base de datos. También existen capacidades para verificar membresía en grupos al ejecutar resolutores o acceder a registros individuales de bases de datos.

Para ayudar a los desarrolladores de React Native a aprender sobre estas tecnologías, existe un esquema GraphQL de muestra integrado que puedes lanzar desde la página de inicio de la consola de AWS AppSync. Esta muestra implementa un esquema GraphQL, aprovisiona tablas de bases de datos y conecta automáticamente consultas, mutaciones y suscripciones. También hay un ejemplo funcional para React Native de AWS AppSync que aprovecha este esquema integrado (así como un ejemplo para React), que te permite tener tus componentes cliente y en la nube funcionando en minutos.

Comenzar es sencillo cuando usas AWSAppSyncClient, que se integra con Apollo Client. El AWSAppSyncClient maneja seguridad y firmas para tu API GraphQL, funcionalidad offline, y el proceso de handshake y negociación de suscripciones:

import AWSAppSyncClient from "aws-appsync";
import { Rehydrated } from 'aws-appsync-react';
import { AUTH_TYPE } from "aws-appsync/lib/link/auth-link";

const client = new AWSAppSyncClient({
url: awsconfig.graphqlEndpoint,
region: awsconfig.region,
auth: {type: AUTH_TYPE.API_KEY, apiKey: awsconfig.apiKey}
});

La consola de AppSync proporciona un archivo de configuración descargable que contiene tu endpoint GraphQL, región de AWS y clave de API. Luego puedes usar el cliente con React Apollo:

const WithProvider = () => (
<ApolloProvider client={client}>
<Rehydrated>
<App />
</Rehydrated>
</ApolloProvider>
);

En este punto, puedes usar consultas GraphQL estándar:

query ListEvents {
listEvents{
items{
__typename
id
name
where
when
description
comments{
__typename
items{
__typename
eventId
commentId
content
createdAt
}
nextToken
}
}
}
}

El ejemplo anterior muestra una consulta con el esquema de aplicación de muestra aprovisionado por AppSync. No solo demuestra interacción con DynamoDB, sino que también incluye paginación de datos (incluyendo tokens cifrados) y relaciones entre tipos como Events y Comments. Como la aplicación está configurada con AWSAppSyncClient, los datos se persisten automáticamente offline y se sincronizan cuando los dispositivos se reconectan.

Puedes ver un análisis profundo de la tecnología del cliente detrás de esto y una demostración de React Native en este video.

Comentarios

El equipo detrás de estas bibliotecas está deseando conocer cómo funcionan estas herramientas y servicios para ustedes. También quieren escuchar qué más podemos hacer para facilitar el desarrollo con React y React Native utilizando servicios en la nube. Pueden contactar al equipo de AWS Mobile en GitHub para AWS Amplify o AWS AppSync.

Implementando la animación de carga de la app de Twitter en React Native

· 11 min de lectura
Eli White
Eli White
Software Engineer @ Meta
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

La app de iOS de Twitter tiene una animación de carga que me gusta bastante.

Cuando la app está lista, el logo de Twitter se expande encantadoramente, revelando la aplicación.

Quería descubrir cómo recrear esta animación de carga con React Native.


Para entender cómo construirla, primero tuve que comprender las distintas partes de la animación. La mejor manera de apreciar los detalles es verla en cámara lenta.

Hay algunos elementos principales que necesitaremos descifrar cómo construir.

  1. Escalar el pájaro.

  2. Mientras el pájaro crece, mostrar la aplicación subyacente

  3. Reducir ligeramente la escala de la aplicación al final

Me tomó bastante tiempo descubrir cómo hacer esta animación.

Empecé con una suposición incorrecta: que el fondo azul y el pájaro de Twitter eran una capa encima de la aplicación y que al crecer el pájaro, se volvía transparente revelando la app debajo. ¡Este enfoque no funciona porque si el pájaro se volviera transparente, mostraría la capa azul, no la aplicación subyacente!

Afortunadamente para ti, querido lector, no tendrás que pasar por la misma frustración que yo. ¡Tienes este tutorial que va directo a lo importante!


La forma correcta

Antes de llegar al código, es importante entender cómo descomponerlo. Para visualizar este efecto, lo recreé en CodePen (incrustado en unos párrafos) para que puedas ver las capas interactivamente.

Hay tres capas principales en este efecto. La primera es la capa de fondo azul. Aunque parece estar encima de la app, en realidad está al fondo.

Luego tenemos una capa blanca lisa. Y finalmente, en primer plano, está nuestra aplicación.


El truco principal de esta animación es usar el logo de Twitter como mask (máscara) que enmascara tanto la app como la capa blanca. No profundizaré mucho en los detalles del enmascaramiento, hay muchos recursos en línea para eso.

Lo básico del enmascaramiento aquí es que los píxeles opacos de la máscara muestran el contenido que enmascaran, mientras que los píxeles transparentes lo ocultan.

Usamos el logo de Twitter como máscara para dos capas: la capa blanca sólida y la capa de la aplicación.

Para revelar la app, escalamos la máscara hasta que sea más grande que toda la pantalla.

Mientras la máscara se escala, aumentamos la opacidad de la capa de la app, mostrándola y ocultando la capa blanca detrás. Para terminar el efecto, iniciamos la capa de la app con escala >1 y la reducimos a 1 al finalizar la animación. Luego ocultamos las capas no-app ya que no se volverán a ver.

Dicen que una imagen vale mil palabras. ¿Cuántas palabras vale una visualización interactiva? Avanza por la animación con el botón "Next Step". Mostrar las capas te da una perspectiva lateral. La cuadrícula ayuda a visualizar las capas transparentes.

Ahora, con React Native

Muy bien. Ahora que sabemos qué construimos y cómo funciona la animación, podemos entrar en código — la verdadera razón por la que estás aquí.

La pieza clave de este rompecabezas es MaskedViewIOS, un componente central de React Native.

import {MaskedViewIOS} from 'react-native';

<MaskedViewIOS maskElement={<Text>Basic Mask</Text>}>
<View style={{backgroundColor: 'blue'}} />
</MaskedViewIOS>;

MaskedViewIOS recibe las props maskElement y children. Los children son enmascarados por el maskElement. Nota que la máscara no necesita ser una imagen, puede ser cualquier vista arbitraria. El comportamiento del ejemplo anterior sería renderizar la vista azul, pero solo sería visible donde las palabras "Basic Mask" aparecen desde el maskElement. Acabamos de crear texto azul complejo.

Lo que queremos hacer es renderizar nuestra capa azul, y luego encima renderizar nuestras capas enmascaradas (la app y la blanca) con el logo de Twitter.

{
fullScreenBlueLayer;
}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Image source={twitterLogo} />
</View>
}>
{fullScreenWhiteLayer}
<View style={{flex: 1}}>
<MyApp />
</View>
</MaskedViewIOS>;

Esto nos dará las capas que vemos a continuación.

Ahora la parte animada

Tenemos todas las piezas necesarias para que esto funcione; el siguiente paso es animarlas. Para que esta animación se sienta fluida, utilizaremos la API Animated de React Native.

Animated nos permite definir nuestras animaciones de manera declarativa en JavaScript. Por defecto, estas animaciones se ejecutan en JavaScript y le dicen a la capa nativa qué cambios hacer en cada fotograma. Aunque JavaScript intentará actualizar la animación en cada fotograma, es probable que no pueda hacerlo lo suficientemente rápido y cause pérdida de fotogramas (jank). ¡No es lo que queremos!

Animated tiene un comportamiento especial para evitar este jank. Tiene una bandera llamada useNativeDriver que envía tu definición de animación desde JavaScript al entorno nativo al inicio de tu animación, permitiendo que el lado nativo procese las actualizaciones sin tener que comunicarse con JavaScript en cada fotograma. La desventaja de useNativeDriver es que solo puedes animar un conjunto específico de propiedades, principalmente transform y opacity. No puedes animar cosas como el color de fondo con useNativeDriver, al menos aún no; iremos agregando más propiedades con el tiempo, y por supuesto siempre puedes enviar un PR para las propiedades que necesites en tu proyecto, beneficiando a toda la comunidad 😀.

Como queremos que esta animación sea fluida, trabajaremos dentro de estas restricciones. Para una mirada más profunda sobre cómo funciona useNativeDriver internamente, consulta nuestra publicación de blog que lo anuncia.

Desglosando nuestra animación

Hay 4 componentes en nuestra animación:

  1. Agrandar el pájaro, revelando la app y la capa blanca sólida

  2. Hacer aparecer gradualmente la app (fade in)

  3. Reducir la escala de la app al final

  4. Ocultar la capa blanca y azul cuando termine

Con Animated, hay dos formas principales de definir tu animación. La primera es usando Animated.timing, que te permite especificar exactamente cuánto tiempo durará tu animación, junto con una curva de easing para suavizar el movimiento. El otro enfoque es usar las API basadas en física como Animated.spring. Con Animated.spring, especificas parámetros como la cantidad de fricción y tensión en el resorte, y dejas que la física ejecute tu animación.

Tenemos múltiples animaciones que queremos ejecutar simultáneamente y que están estrechamente relacionadas. Por ejemplo, queremos que la app comience a aparecer gradualmente mientras la máscara está a medio revelar. Debido a esta relación cercana, usaremos Animated.timing con un único Animated.Value.

Animated.Value es un envoltorio alrededor de un valor nativo que Animated usa para conocer el estado de una animación. Normalmente querrás tener solo uno de estos para una animación completa. La mayoría de componentes que usan Animated almacenarán este valor en el estado.

Como estoy pensando en esta animación como pasos que ocurren en diferentes momentos a lo largo de la secuencia completa, comenzaremos nuestro Animated.Value en 0 (representando 0% completado) y terminaremos en 100 (representando 100% completado).

El estado inicial de nuestro componente será el siguiente:

state = {
loadingProgress: new Animated.Value(0),
};

Cuando estemos listos para comenzar la animación, le diremos a Animated que anime este valor hasta 100.

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true, // This is important!
}).start();

Luego intento calcular una estimación aproximada de las diferentes partes de las animaciones y los valores que deberían tener en distintas etapas de la animación general. A continuación se muestra una tabla con las diferentes partes de la animación y los valores que considero deberían tener en distintos puntos a medida que avanzamos en el tiempo.

La máscara del pájaro de Twitter debe comenzar con escala 1, y se hace más pequeña antes de aumentar rápidamente de tamaño. Así que al 10% de la animación, debería tener un valor de escala de 0.8 antes de aumentar hasta escala 70 al final. Elegir 70 fue bastante arbitrario, la verdad; necesitaba ser lo suficientemente grande para que el pájaro revelara completamente la pantalla y 60 no alcanzaba 😀. Algo interesante de esta parte es que cuanto más alto sea el número, más rápido parecerá crecer porque debe llegar allí en el mismo tiempo. Este número requirió varias pruebas hasta lograr que se viera bien con este logo. Logotipos o dispositivos de diferentes tamaños requerirán que esta escala final sea diferente para garantizar que toda la pantalla quede revelada.

La aplicación debe permanecer opaca durante un tiempo, al menos mientras el logo de Twitter se hace más pequeño. Según la animación oficial, quiero comenzar a mostrarla cuando el pájaro esté a mitad de camino en su aumento y revelarla completamente bastante rápido. Así que al 15% comenzamos a mostrarla, y al 30% de la animación total ya es completamente visible.

La escala de la aplicación comienza en 1.1 y se reduce hasta su escala normal al final de la animación.

Y ahora, en código.

Esencialmente, lo que hicimos anteriormente es mapear los valores del porcentaje de progreso de la animación a los valores de las piezas individuales. Hacemos esto con Animated usando .interpolate. Creamos 3 objetos de estilo diferentes, uno para cada parte de la animación, usando valores interpolados basados en this.state.loadingProgress.

const loadingProgress = this.state.loadingProgress;

const opacityClearToVisible = {
opacity: loadingProgress.interpolate({
inputRange: [0, 15, 30],
outputRange: [0, 0, 1],
extrapolate: 'clamp',
// clamp means when the input is 30-100, output should stay at 1
}),
};

const imageScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 10, 100],
outputRange: [1, 0.8, 70],
}),
},
],
};

const appScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 100],
outputRange: [1.1, 1],
}),
},
],
};

Ahora que tenemos estos objetos de estilo, podemos usarlos al renderizar el fragmento de la vista que vimos antes en la publicación. Ten en cuenta que solo Animated.View, Animated.Text y Animated.Image pueden usar objetos de estilo que empleen Animated.Value.

const fullScreenBlueLayer = (
<View style={styles.fullScreenBlueLayer} />
);
const fullScreenWhiteLayer = (
<View style={styles.fullScreenWhiteLayer} />
);

return (
<View style={styles.fullScreen}>
{fullScreenBlueLayer}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Animated.Image
style={[styles.maskImageStyle, imageScale]}
source={twitterLogo}
/>
</View>
}>
{fullScreenWhiteLayer}
<Animated.View
style={[opacityClearToVisible, appScale, {flex: 1}]}>
{this.props.children}
</Animated.View>
</MaskedViewIOS>
</View>
);

¡Yay! Ahora tenemos las partes de la animación luciendo como queremos. Solo nos queda limpiar nuestras capas azul y blanca que nunca volverán a verse.

Para saber cuándo podemos limpiarlas, necesitamos saber cuándo se completa la animación. Por suerte, cuando llamamos a Animated.timing, .start recibe un callback opcional que se ejecuta cuando la animación termina.

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true,
}).start(() => {
this.setState({
animationDone: true,
});
});

Ahora que tenemos un valor en state para saber si hemos terminado con la animación, podemos modificar nuestras capas azul y blanca para que lo utilicen.

const fullScreenBlueLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenBlueLayer]} />
);
const fullScreenWhiteLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenWhiteLayer]} />
);

¡Voilà! Nuestra animación ahora funciona y limpiamos las capas no utilizadas una vez que termina. ¡Hemos construido la animación de carga de la app de Twitter!

¡Pero espera, el mío no funciona!

No te preocupes, querido lector. Yo también odio cuando las guías solo dan fragmentos de código sin proporcionar el código completo.

Este componente ha sido publicado en npm y está en GitHub como react-native-mask-loader. Para probarlo en tu teléfono, está disponible en Expo aquí:

Más lecturas / Crédito extra

  1. Este gitbook es un gran recurso para aprender más sobre Animated después de leer la documentación de React Native.

  2. La animación real de Twitter parece acelerar la revelación de la máscara hacia el final. Intenta modificar el loader para usar una función de easing diferente (¡o un spring!) para igualar mejor ese comportamiento.

  3. La escala final actual de la máscara está codificada y probablemente no revelará toda la aplicación en una tablet. Calcular la escala final basada en el tamaño de pantalla y el tamaño de la imagen sería un PR increíble.

React Native Mensual #6

· 4 min de lectura
Tomislav Tenodi
Fundador en Speck
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

¡La reunión mensual de React Native sigue con fuerza! Asegúrate de revisar la nota al final de esta publicación para las próximas sesiones.

Expo

  • Felicitaciones a Devin Abbott y Houssein Djirdeh por el prelanzamiento de su libro "Full Stack React Native". Te guía en el aprendizaje de React Native mediante la construcción de varias aplicaciones pequeñas.

  • Lanzada la primera versión (experimental) de reason-react-native-scripts para ayudar a las personas a probar fácilmente ReasonML.

  • ¡El SDK 24 de Expo ha sido lanzado! Utiliza React Native 0.51 e incluye un conjunto de nuevas funciones y mejoras: empaquetado de imágenes en aplicaciones independientes (¡sin necesidad de caché en la primera carga!), API de manipulación de imágenes (recortar, redimensionar, rotar, voltear), API de detección facial, nuevas funciones de canales de lanzamiento (establecer el lanzamiento activo para un canal dado y revertir), panel web para rastrear compilaciones de aplicaciones independientes, y una corrección para un error persistente con la implementación de OpenGL en Android y el multitarea de Android, por nombrar solo algunas cosas.

  • Estamos asignando más recursos a React Navigation a partir de este enero. Creemos firmemente que es posible y deseable construir navegación en React Native usando solo componentes de React y primitivas como Animated y react-native-gesture-handler, y estamos muy entusiasmados con algunas de las mejoras que planeamos. Si buscas contribuir a la comunidad, revisa react-native-maps y react-native-svg, ¡ambos podrían usar ayuda!

Infinite Red

Microsoft

  • Se ha iniciado un pull request para migrar el núcleo del puente de React Native Windows a .NET Standard, haciéndolo efectivamente independiente del sistema operativo. La esperanza es que muchas otras plataformas .NET Core puedan extender el puente con sus propios modelos de subprocesos, entornos de ejecución JavaScript y UIManagers (piensa en JavaScriptCore, Xamarin.Mac, Linux Gtk# y opciones Samsung Tizen).

Wix

  • Detox

    • Para escalar con pruebas E2E, queremos minimizar el tiempo en CI; estamos trabajando en soporte de paralelización para Detox.
    • Se ha enviado un pull request para habilitar soporte para compilaciones de sabores personalizados, mejorando el soporte para mocks en E2E.
  • DetoxInstruments

    • Desarrollar la función principal de DetoxInstruments está siendo un desafío complejo: capturar backtraces de JavaScript en cualquier momento requiere una implementación personalizada de JSCore que soporte suspensión del hilo JS. Las pruebas internas del perfilador en la app de Wix revelaron hallazgos interesantes sobre el comportamiento del hilo JS.
    • El proyecto aún no es lo suficientemente estable para uso general, pero estamos trabajando activamente y esperamos anunciarlo pronto.
  • React Native Navigation

    • ¡El desarrollo de la V2 ha acelerado significativamente! Pasamos de tener 1 desarrollador dedicando 20% de su tiempo a contar con 3 desarrolladores trabajando a tiempo completo.
  • Rendimiento en Android

    • Reemplazar la versión antigua de JSCore incluida en RN por la más reciente (versión de punta del proyecto webkitGTK con configuración JIT personalizada) generó un 40% de mejora en el hilo JS. El próximo paso es compilar una versión de 64 bits. Este trabajo se basa en los scripts de compilación JSC para Android. Puedes seguir su estado actual aquí.

Próximas sesiones

Estamos evaluando reorientar estas reuniones para enfocarnos en temas específicos (como navegación, migración de módulos a repositorios independientes o documentación). Creemos que así podremos contribuir mejor a la comunidad de React Native. Esta modalidad podría implementarse en la próxima sesión. ¡Tuitea qué temas te gustaría que abordáramos!

React Native Mensual #5

· 4 min de lectura
Tomislav Tenodi
Fundador en Speck
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

¡La reunión mensual de React Native continúa! Veamos en qué están trabajando nuestros equipos.

Callstack

  • Hemos trabajado en la CI de React Native. Lo más importante es que migramos de Travis a CircleCI, dejando a React Native con una única canalización de CI unificada.

  • Organizamos el Hacktoberfest - edición React Native donde, junto a los participantes, intentamos enviar numerosas pull requests a proyectos de código abierto.

  • Seguimos desarrollando Haul. El mes pasado lanzamos dos nuevas versiones con soporte para webpack 3. Planeamos añadir compatibilidad con CRNA y Expo, además de mejorar el HMR. Nuestra hoja de ruta está pública en el issue tracker. Si deseas sugerir mejoras o dar feedback, ¡avísanos!

Expo

  • Lanzamos Expo SDK 22 (con React Native 0.49) y actualizamos CRNA para esta versión.

    • Incluye API mejorada para pantallas de inicio, soporte básico para ARKit, API "DeviceMotion", compatibilidad con SFAuthenticationSession en iOS11 y más.
  • Tus snacks ahora admiten múltiples archivos JavaScript y puedes subir imágenes y otros assets arrastrándolos directamente al editor.

  • Contribuimos a react-navigation para añadir soporte para iPhone X.

  • Enfocamos nuestra atención en problemas comunes al crear aplicaciones grandes con Expo. Por ejemplo:

    • Soporte nativo para despliegues en múltiples entornos: staging, producción y canales personalizados. Los canales admitirán rollbacks y configuración de releases activas. Avísanos si quieres ser tester inicial @expo_io.
    • También mejoramos nuestra infraestructura para construir apps independientes, añadiendo soporte para incluir imágenes y assets no-code en builds finales manteniendo la capacidad de actualizarlos vía OTA.

Facebook

  • Mejor soporte RTL:

    • Introducimos estilos sensibles a la dirección:
      • Posición:
        • (left|right) → (start|end)
      • Márgenes:
        • margin(Left|Right) → margin(Start|End)
      • Relleno:
        • padding(Left|Right) → padding(Start|End)
      • Bordes:
        • borderTop(Left|Right)Radius → borderTop(Start|End)Radius
        • borderBottom(Left|Right)Radius → borderBottom(Start|End)Radius
        • border(Left|Right)Width → border(Start|End)Width
        • border(Left|Right)Color → border(Start|End)Color
    • Los significados de "left" y "right" se intercambiaban en RTL para estilos de posición, márgenes, relleno y bordes. En unos meses eliminaremos este comportamiento haciendo que "left" siempre signifique izquierda y "right" siempre signifique derecha. Estos cambios son breaking y están ocultos tras un flag. Usa I18nManager.swapLeftAndRightInRTL(false) en tus componentes para activarlos.
  • Trabajamos en tipar nuestros módulos nativos internos con Flow y usarlos para generar interfaces en Java y protocolos en ObjC que las implementaciones nativas deben seguir. Esperamos que este sistema de codegen sea open source el próximo año como muy pronto.

Infinite Red

  • Nueva herramienta de código abierto para ayudar a React Native y otros proyectos. Más información aquí.

  • Actualizando Ignite para una nueva versión de boilerplate (Nombre en código: Bowser)

Shoutem

  • Mejorando el flujo de desarrollo en Shoutem. Queremos simplificar el proceso desde crear una app hasta la primera pantalla personalizada, haciéndolo realmente sencillo para reducir la barrera de entrada de nuevos desarrolladores React Native. Preparamos talleres para probar nuevas funcionalidades. También mejoramos Shoutem CLI para soportar estos nuevos flujos.

  • Shoutem UI recibió mejoras en componentes y correcciones de errores. También verificamos compatibilidad con las últimas versiones de React Native.

  • La plataforma Shoutem recibió actualizaciones importantes, con nuevas integraciones disponibles como parte del proyecto open-source de extensiones. Nos entusiasma ver desarrollo activo en extensiones de Shoutem por otros desarrolladores. Contactamos activamente ofreciendo asesoría y guía sobre sus extensiones.

Próxima sesión

La próxima sesión está programada para el miércoles 6 de diciembre de 2017. No dudes en contactarme en Twitter si tienes sugerencias para mejorar los resultados de estas reuniones.