11 errores que cometí durante el desarrollo de la aplicación React Native / Redux

Después de trabajar casi un año con React Native, decidí describir los errores que cometí al ser principiante.

1. Estimación incorrecta

Realmente: ¡la estimación para la primera aplicación React Native (RN) era completamente incorrecta! ¡Totalmente!

1) ¡Necesita estimar el diseño para las versiones de iOS y Android por separado! Por supuesto, habrá muchos componentes reutilizados, pero también puede haber diferentes diseños. O incluso las estructuras de la página de la aplicación para iOS y Android pueden diferir totalmente.

2) Cuando estima los formularios, también es mejor estimar el diseño de validación. Cuando desarrolla una aplicación en RN, necesita escribir más código que cuando desarrolla aplicaciones híbridas usando Cordova, por ejemplo.

3) Si necesita crear una aplicación basada en una aplicación web que ya tiene backend (por lo que necesita usar una API ya construida), asegúrese de verificar todos los puntos finales proporcionados por el backend. Porque necesitará manejar la lógica en su aplicación, y debe codificarse correctamente. Comprenda la estructura de la base de datos, cómo se conectan las entidades, etc. Si la comprende, puede planificar su tienda redux correctamente (se mencionará a continuación)

2. Intentar usar componentes ya construidos (botones, pies de página, encabezados, entradas, textos) - Solo mi opinión.

Si verifica en Google los componentes ya construidos, como botones, pies de página, etc., verá que hay muchas bibliotecas que puede usar. Es realmente útil si no tiene un diseño de diseño especial. Simplemente construya una página usando estos bloques y eso es todo. Pero si tiene un diseño especial y en este diseño los botones se ven diferentes, deberá establecer estilos personalizados para cada botón. Puede ser un poco complicado. Por supuesto, puede envolver componentes ya construidos en sus componentes y establecer estilos personalizados para ellos allí. Pero creo que será más fácil y valioso para usted crear sus propios componentes usando View, Text, TouchableOpacity y otros módulos de RN. ¿Por qué? Comprenderá cómo trabajar con RN, tendrá más práctica. Y estará seguro de que la versión de los componentes que ha creado usted mismo no cambiará. Entonces, no depende de las versiones de módulos externos.

3. No separando el diseño para iOS y Android

Este punto es útil solo si tiene diseños diferentes para las versiones de iOS y Android. Si no, simplemente puede usar la API de plataforma proporcionada por RN para hacer pequeñas comprobaciones dependiendo de la plataforma del dispositivo.

Si los diseños son completamente diferentes, mejor separe los diseños en diferentes archivos.

Si nombra un archivo como index.ios.js, al armar la compilación para iOS, RN usará este archivo para mostrar el diseño de iOS. La misma historia es para index.android.js.

Puede preguntar "¿Qué pasa con la duplicación de código?". Sí. Puede mover código duplicado a ayudantes. Y luego solo reutiliza estos ayudantes.

4. Planificación incorrecta de la tienda redux.

Un gran error.

Cuando planifica su aplicación, piensa mucho en el diseño. Menos: sobre el manejo de datos.

Redux nos ayuda a almacenar datos correctamente. Si la tienda redux se planifica correctamente, es una herramienta poderosa para administrar los datos de la aplicación. Si no, puede estropear todo.

Cuando solo comencé a construir aplicaciones RN, pensé en los reductores como un almacenamiento de datos para cada contenedor. Por lo tanto, si tiene páginas de inicio de sesión, contraseña olvidada, lista de tareas pendientes, debe haber sus reductores: inicio de sesión, olvidado, lista de tareas.

Después de trabajar un poco con la planificación de esta tienda, descubrí que en mi caso no era tan fácil administrar los datos. Tenía una página de detalles de tareas pendientes. Y utilizando esta planificación de la tienda, proporcioné el reductor ToDoDetails. ¡Y eso fue un ENORME error! ¿Por qué?

Cuando selecciono un elemento de la lista de tareas pendientes, necesito pasar datos al reductor de ToDoDetails. Significaba usar acciones adicionales para enviar datos al reductor. Y no fue muy cómodo.

Después de investigar un poco, decidí planificar la tienda de manera diferente. Y la estructura era así:

1.Aut
2 toodos
3 amigos

Auth se usa para almacenar el token de autorización. Eso es todo

Y los reductores Todos y Amigos se utilizan para almacenar entidades, ya que es fácil de entender a partir de los nombres. Y cuando voy a la pantalla de detalles de ToDo, solo busco en todos los ToDos por id. Eso es todo.

Para estructuras mucho más complicadas, realmente recomiendo usar esta planificación. Siempre entenderás qué es qué. Y dónde encontrar esto o aquello.

5. Estructura incorrecta del proyecto

Siempre es difícil planificar la estructura del proyecto cuando solo eres un principiante.

En primer lugar, entienda, ¿su aplicación es grande? ¿Realmente grande? ¿Enorme? O pequeño?

¿Cuántas pantallas tienes en tu aplicación? 20? 30? 10? 5? Hola pantalla del mundo?

La primera estructura que conocí y comencé a implementar fue la siguiente:

Las acciones y reductores están separados de los contenedores.

Bueno, esto es bueno si no tienes una gran aplicación. Por ejemplo, 10 pantallas como máximo. Si es más grande, considere usar algo como esto:

Acciones y reductores almacenados con contenedores con los que están conectados

¿Cuál es la diferencia? Como puede ver, el primer tipo nos propone almacenar acciones y reductores por separado del contenedor. Segundo: guárdelos juntos. Si la aplicación es pequeña, es más útil almacenar módulos redux separados de los contenedores. Si no, con contenedores, siempre sabrá qué acción concierne a este contenedor.

Si tiene estilos comunes (como encabezado, pie de página, botones), puede crear una carpeta separada llamada "estilos", establecer allí un archivo index.js y escribir estilos comunes allí. Y luego simplemente reutilícelos en cada página.

Hay muchas estructuras diferentes. Solo debe averiguar: cuál de ellos se ajustará mejor a sus solicitudes.

6. Estructura de contenedor incorrecta. No usar ideología de componentes inteligentes / tontos desde el principio

Cuando comience a trabajar con RN e inicie su primera aplicación, ya hay algo de código en el archivo index.ios.js. Si lo marca, verá que los estilos se almacenan en un objeto separado, no hay evaluaciones en el método de representación y todo está construido con módulos proporcionados por RN (Ver, Texto).

En la vida real, necesitará utilizar muchos componentes, no solo proporcionados por RN. Construirá muchos componentes para reutilizarlos mientras construye contenedores.

Considere este componente:

import React, {Component} de "react";
importar {
   Texto,
   Entrada de texto,
   Ver,
   TouchableOpacity
} de "react-native";
importar estilos desde "./styles.ios";
exportar clase predeterminada SomeContainer extiende Componente {
   constructor (accesorios) {
       super (accesorios);
       this.state = {
           nombre de usuario: nulo
       }
   }
   _usernameChanged (evento) {
       this.setState ({
           nombre de usuario: event.nativeEvent.text
       });
    }
   _enviar(){
       if (this.state.username) {
           console.log (`¡Hola, $ {this.state.username}!`);
       }
       más{
           console.log ("Ingrese nombre de usuario");
       }
    }
    render () {
        regreso (
            
                
                    
                        fuente = {this.props.image}
                        estilo = {styles.avatar} />
                
                
                    
                        
                            Nombre de usuario
                        
                        
                         onChange = {this._usernameChanged.bind (this)}
                         valor = {this.state.username} />
                    
                
                
                    
                        
                            Enviar
                        
                    
                
            
        );
    }
}

¿Cómo se ve?

Bueno, como puede ver, todos los estilos se almacenan en un módulo separado, bien. No hay duplicación de código (por ahora) - bien. Pero, ¿con qué frecuencia usamos un solo campo en forma? No estoy seguro de que sea tan a menudo. Además, el componente del botón, el que está envuelto en TouchableOpacity, se puede separar, para que podamos reutilizarlo en el futuro. Y sobre imagen. También podemos reutilizar este bloque en el futuro, por lo que debería trasladarse a un componente separado.

Y después de hacer todos los cambios, obtendremos esto:

import React, {Component, PropTypes} de 'react';
importar {
    Texto,
    Entrada de texto,
    Ver,
    TouchableOpacity
} de 'react-native';
importar estilos desde './styles.ios';
La clase Avatar extiende el componente {
    constructor (accesorios) {
        super (accesorios);
    }
    hacer(){
        if (this.props.imgSrc) {
            regreso(
                
                    
                        fuente = {this.props.imgSrc}
                        estilo = {styles.avatar} />
                
             )
         }
         volver nulo;
    }
}
Avatar.propTypes = {
    imgSrc: PropTypes.object
}
class FormItem extiende Componente {
    constructor (accesorios) {
        super (accesorios);
    }
    hacer(){
        let title = this.props.title;
        regreso(
            
                
                    {título}
               
               
                   onChange = {this.props.onChange}
                   valor = {this.props.value} />
            
        )
    }
}
FormItem.propTypes = {
    título: PropTypes.string,
    valor: PropTypes.string,
    onChange: PropTypes.func.isRequired
}
El botón de clase extiende el componente {
    constructor (accesorios) {
        super (accesorios);
    }
    hacer(){
        let title = this.props.title;
        regreso(
            
                
                    
                        {título}
                    
                
            
        )
    }
}
Button.propTypes = {
    título: PropTypes.string,
    onPress: PropTypes.func.isRequired
}
exportar clase predeterminada SomeContainer extiende Componente {
    constructor (accesorios) {
        super (accesorios);
        this.state = {
            nombre de usuario: nulo
        }
    }
    _usernameChanged (evento) {
        this.setState ({
            nombre de usuario: event.nativeEvent.text
        });
    }
    _enviar(){
        if (this.state.username) {
            console.log (`¡Hola, $ {this.state.username}!`);
        }
        más{
            console.log ('Por favor, ingrese nombre de usuario');
        }
    }
    render () {
        regreso (
            
                
                
                    
                      title = {"Nombre de usuario"}
                      valor = {this.state.username}
                      onChange = {this._usernameChanged.bind (this)} />
                
                
                    title = {"Enviar"}
                    onPress = {this._submit.bind (this)} />
            
        );
    }
}

Sí, tal vez haya un poco más de código ahora, porque agregamos envoltorios para los componentes Avatar, FormItem y Button, pero ahora podemos reutilizar todos estos componentes donde queramos. Podemos moverlos a módulos separados e importarlos donde sea que los necesitemos. Podemos agregarles algunos otros accesorios, por ejemplo: style, textStyle, onLongPress, onBlur, onFocus. Y estos componentes son totalmente personalizables.

Pero asegúrese de no sumergirse profundamente en la personalización de un componente pequeño tanto que crecerá hasta el tamaño de Godzilla. Es malo y difícil de leer. De Verdad. E incluso si en este momento la idea de agregar alguna nueva propiedad parece ser la forma más fácil de resolver tareas, en el futuro esta pequeña propiedad puede ser confusa cuando lea el código.

Y sobre la ideología inteligente / tonta. Mira esto:

El botón de clase extiende el componente {
    constructor (accesorios) {
        super (accesorios);
    }
    _setTitle () {
        const {id} = this.props;
        interruptor (id) {
            caso 0:
                devolver 'Enviar';
            caso 1:
                regresar 'Borrador';
            caso 2:
                volver 'Eliminar';
            defecto:
                devolver 'Enviar';
         }
    }
    hacer(){
        let title = this._setTitle ();
        regreso(
            
                
                    
                        {título}
                    
               
           
        )
    }
}
Button.propTypes = {
    id: PropTypes.number,
    onPress: PropTypes.func.isRequired
}
exportar clase predeterminada SomeContainer extiende Componente {
    constructor (accesorios) {
        super (accesorios);
        this.state = {
            nombre de usuario: nulo
        }
    }
    _enviar(){
        if (this.state.username) {
            console.log (`¡Hola, $ {this.state.username}!`);
        }
        más{
            console.log ('Por favor, ingrese nombre de usuario');
        }
    }
    render () {
        regreso (
            
                
                    id = {0}
                    onPress = {this._submit.bind (this)} />
            
        );
    }
}

Como puede ver, hemos "actualizado" nuestro componente Button. ¿Qué ha cambiado? Bueno, reemplazamos el "título" de la propiedad con una nueva propiedad llamada "id". Y ahora tenemos cierta "flexibilidad" en nuestro componente Button. Pase 0: mostrará "Enviar". Pase 2: "Eliminar". Pero es realmente malo.

El botón se creó como un componente tonto: para mostrar solo los datos que se le pasaron y para hacer cosas que se le dijeron en un nivel superior. Los componentes tontos no deberían saber nada sobre las cosas que los rodean. Simplemente haz y muestra lo que se les dijo. Y después de nuestra pequeña "actualización" lo hicimos inteligente. Y es malo ¿Por qué?

¿Qué sucederá si pasaremos 5 como id a este componente? Tendremos que actualizarlo para que funcione con esta opción. Y así sucesivamente y así sucesivamente. Los componentes tontos solo deberían mostrarse y hacer lo que se les dijo. Eso es todo.

7. Estilos en línea

Después de trabajar un poco con el diseño en RN, enfrenté un problema con los estilos de escritura en línea. Me gusta esto:

render () {
    regreso (
        
            
                title = {"Enviar"}
                onPress = {this._submit.bind (this)} />
        
    );
}

Cuando escribes así al principio piensas: “OK. Después de verificar el diseño en el simulador, moverá los estilos a un módulo separado si está bien ”. Y tal vez eso es lo que realmente querías hacer. Pero ... lamentablemente no va a suceder. Al menos, nadie en mi equipo lo hizo hasta que se les recordó.

Siempre escriba estilos en módulos separados. Lo mantendrá a salvo del estilo en línea.

8. Validación de formularios con redux

Eso fue un error en mi proyecto. Quizás sea útil en los tuyos.

Para validar el formulario con una ayuda de redux, necesitaba crear una acción, un tipo de acción, un campo separado en el reductor. Y fue realmente molesto.

Así que decidí hacerlo solo con la ayuda del estado. Sin reductores, tipos, etc. Solo funciones puras en el nivel del contenedor. Eso me ayudó mucho: eliminó las funciones innecesarias del archivo de acción, el archivo reductor. Sin manipulaciones con la tienda.

Eso es lo que se ajustaba a mi proyecto.

9. Confiar en zIndex DEMASIADO

Mucha gente viene al desarrollo RN desde la web. Y en la web hay un CSS que tiene una propiedad de índice z. Nos ayuda a mostrar la capa que queremos en el nivel que queremos. En la web es realmente genial.

En RN no había tal característica al principio. Pero luego se agregó. Y así empecé a usarlo. Al principio fue realmente fácil. Renderice la capa en el orden que desee y solo establezca la propiedad zIndex como estilo. Y funcionará. Pero después de probarlo en Android ... Ahora solo estructuro mis capas para que se muestren. Ese es el mejor zIndex que puedes hacer. De Verdad.

10. No leer el código de módulos externos

Cuando quiere ahorrar tiempo, utiliza módulos externos. A menudo tienen documentación. Simplemente toma información y la usa.

Pero a veces este módulo puede romperse. O no funciona como se describió. Es por eso que necesitas leer el código. Comprenderás lo que está mal. Quizás el módulo sea malo. O tal vez solo lo estás usando incorrectamente. Además, aprenderá a construir su propio módulo si lee el código de otros módulos.

11. Conocimiento de PanResponder y API animadas.

RN nos brinda la capacidad de crear aplicaciones completamente nativas. ¿Y qué hace que la aplicación se sienta nativa? Diseños, gestos, animaciones.

Y si los diseños se proporcionan de forma predeterminada, cuando utiliza View, Text, TextInput y otros módulos RN, PanResponder y las API animadas deben manejar los gestos y las animaciones.

Si viniste de la web (como lo hice), te dará un poco de miedo, obtén gestos de los usuarios, cuando comenzó, cuando terminó, presiona largamente, presiona brevemente. Además, no está lo suficientemente claro: cómo animar algo en RN.

Aquí hay un componente de botón que construí con la ayuda de PanResponder y Animated. Este botón fue creado para capturar los gestos del usuario. Por ejemplo, el usuario presiona el elemento y luego arrastra el dedo hacia un lado. Con una ayuda de Animated API, se construyó el cambio de opacidad cuando se presionó el botón:

'uso estricto';
import React, {Component, PropTypes} de 'react';
importar {Animated, View, PanResponder, Easing} desde 'react-native';
importar momento desde 'momento';
exportar clase predeterminada El botón extiende el componente {
    constructor (accesorios) {
        super (accesorios);
        this.state = {
            marca de tiempo: 0
        };
        this.opacityAnimated = new Animated.Value (0);
        this.panResponder = PanResponder.create ({
   onMoveShouldSetPanResponderCapture: (evt, movementState) => true,
   onStartShouldSetResponder :() => verdadero,
   onStartShouldSetPanResponder: () => verdadero,
   onMoveShouldSetPanResponder: (evt, gestoState) => verdadero,
   onPanResponderMove: (e, gesto) => {},
   onPanResponderGrant: (evt, movementState) => {
   / ** ESTE EVENTO SE LLAMA CUANDO PULSAMOS EL BOTÓN ** /
       this._setOpacity (1);
       this.setState ({
           marca de tiempo: momento ()
       });
       this.long_press_timeout = setTimeout (() => {
            this.props.onLongPress ();
       }, 1000);
   },
   onPanResponderStart: (e, gestState) => {},
   onPanResponderEnd: (e, gestState) => {},
   onPanResponderTerminationRequest: (evt, movementState) => verdadero,
   onPanResponderRelease: (e, gesto) => {
   / ** ESTE EVENTO SE LLAMA CUANDO LIBERAMOS EL BOTÓN ** /
       let diff = moment (). diff (moment (this.state.timestamp));
       si (diff <1000) {
           this.props.onPress ();
       }
       clearTimeout (this.long_press_timeout);
       this._setOpacity (0);
       this.props.releaseBtn (gesto);
   }
     });
    }
    _setOpacity (valor) {
    / ** ESTABLECE LA OPACIDAD DEL BOTÓN ** /
        Animated.timing (
        this.opacityAnimated,
        {
            toValue: valor,
            duración: 80,
        }
        ).comienzo();
    }
    hacer(){
        let longPressHandler = this.props.onLongPress,
            pressHandler = this.props.onPress,
            imagen = this.props.image,
            opacity = this.opacityAnimated.interpolate ({
              inputRange: [0, 1],
              outputRange: [1, 0.5]
            });
        regreso(
            
                
                   {... this.panResponder.panHandlers}
                   style = {[styles.mainBtn, this.props.style, {opacity: opacity}]}>
                    {imagen}
               
            
        )
    }
}
Button.propTypes = {
    onLongPress: PropTypes.func,
    onPressOut: PropTypes.func,
    onPress: PropTypes.func,
    estilo: PropTypes.object,
    imagen: PropTypes.object
};
Button.defaultProps = {
    onPressOut: () => {console.log ('onPressOut no está definido'); },
    onLongPress: () => {console.log ('onLongPress no está definido'); },
    onPress: () => {console.log ('onPress no está definido'); },
    estilo: {},
    imagen: nulo
};
const styles = {
    mainBtn: {
        ancho: 55,
        altura: 55,
        backgroundColor: 'rgb (255,255,255)',
    }
};

Primero, iniciamos la instancia del objeto PanResponder. Allí establecemos diferentes manejadores. Lo que nos interesa es los manejadores onPanResponderGrand (llamado cuando el usuario toca el botón) y onPanResponderRelease (llamado cuando el usuario quita el dedo de la pantalla);

También configuramos una instancia de objeto Animated que nos ayuda a trabajar con animación. Establezca su valor en cero; Luego definimos el método _setOpacity, que cambia el valor de this.opacityAnimated. Y antes de renderizar, interpolamos this.opacityAnimated to normal opacity value. No utilizamos el módulo Ver sino Animated.View para usar un valor de opacidad cambiado dinámicamente. Eso es todo.

Como puede ver, no es difícil entender lo que está sucediendo. Por supuesto, deberá leer la documentación sobre estas API para que sus aplicaciones sean perfectas. Pero espero que este ejemplo te ayude a comenzar.

React Native es asombroso. Puedes hacer casi todo con él. Si no, puede hacerlo en Swift / Objective C o Java y luego simplemente portarlo a React Native.

Hay una gran comunidad, muchas soluciones, componentes, estructuras, etc. Y cuando se desarrolle, seguramente cometerá errores. Así que espero que este artículo te ayude a evitar algunos de ellos.