Herel Odin

Desarrollador Web

¿Cómo programar actions en react native y redux para iniciar sesión a través de un rest api?

by herel Last updated octubre 31, 2020 Comments

Para  tener un poco de contexto ya creamos un api rest con las siguientes opciones:

  1. Registrar usuarios
  2. Iniciar sesión
  3. Actualizar perfil con jsonwebtokens

Tambien creamos una aplicacion en react native que hasta el momento tiene un formulario para registrar usuarios

Ahora vamos a crear un formulario para iniciar sesión, si los datos son correctos  almacenamos  el usuario y token en el teléfono.

1.- Cómo crear formulario para iniciar sesión

Como vimos en el tutorial anterior creamos un formulario para registrar usuarios ahora vamos a crear otro screen donde vamos a reutilizar el component Input y colocar el campo  email y password

Creamos el componente en la siguiente ruta src/screens/Login.js

import React, { Component } from 'react';
import { View } from 'react-native';
import Input from '../components/Input';
import Button from '../components/Button';
import LinearGradient from 'react-native-linear-gradient';
import {
    container
} from '../config/styles';

class Login extends Component{
    
    constructor(props){
        super(props);
        this.state = {
            email       : '',
            password  : '',
            loader       : false
        }
    }

    _getDisabled(){
        let disabled = false;
        if(!this.state.email)
            disabled = true;
        if(!this.state.password || this.state.password.length <=5)
            disabled = true;
        if(this.state.loader)
            disabled = true;
        return disabled;
    }

    render(){
        return (
            <LinearGradient
                colors={['#441E35','#320622']}
                style={container}>

                <Input
                    placeholder="Correo electrónico"
                    onChangeText={(email) => this.setState({email})}
                    value={this.state.email}
                />
                <Input
                    secureTextEntry={true}
                    placeholder="Contraseña"
                    onChangeText={(password) => this.setState({password})}
                    value={this.state.password}
                />
                <Button
                    disabled={this._getDisabled()}
                    text="Iniciar Sesión"
                />
            </LinearGradient>
        )
    }
}


export default Login;

El resultado debe ser el siguiente:

1.1.-  Patalla de inicio

Ya tenemos 2 screens una para registrar usuarios y otra para iniciar sesión pero nuestra app carga por default el screen de registrar usuarios.

vamos a crear un screen llamado home donde vamos agregar 2 botones para enviar a sus screens correspondientes.

import React, { Component } from 'react';
import { View } from 'react-native';
import Button from '../components/Button';
import LinearGradient from 'react-native-linear-gradient';
import {
    container
} from '../config/styles';

class Home extends Component{
    
    constructor(props){
        super(props);
        this.state = { }
    }

    _go(routeName){
        this.props.navigation.navigate(routeName);
    }

    render(){
        return (
            <LinearGradient
                colors={['#441E35','#320622']}
                style={container}>
                <Button
                    onPress={() => this._go('registro')}
                    text="¡Registrate!"
                />
                <Button
                    onPress={() => this._go('login')}
                    text="¿Ya tienes una cuenta? Inica sesión"
                />
            </LinearGradient>
        )
    }
}


export default Home;

Como podemos observar reutilzamos el componente Button y solo le pasamos el texto y la funcion onPress que al hacer touch en el boton ejcuta la funcion _go pero cada una con diferente parametro que a su vez le dice a react nativation que haga la transición al screen login o registro.

1.2.- Cómo añadir screens al StackNavigator de react-navigation

Ya tenemos el screen Home pero ahora debemos hacer que la app cargue por default la pantalla con los dos botones para esto vamos al archivo index.js donde tenemos el StackNavigator e importamos el component Home

import React, { Component } from 'react';
import { StackNavigator } from 'react-navigation';
import Form from './screens/Form.js';
import Login from './screens/Login';
import Home from './screens/Home';


const Navigation = StackNavigator({
    home : {
        screen : Home
    },
    registro : {
        screen : Form
    },
    login : {
        screen : Login
    }
},{
    headerMode: 'none'
});

export default Navigation;

el resultado  es el siguiente:

2.- Cómo crear actions en redux

Como vimos en el tutorial para registrar usuarios creamos la carpeta actions  pero hasta el moment no tenemos nada, es tiempo de crear un action que va hacer una peticion de tipo POST a la api  para enviar el email y contraseña del usuario.

Creamos el archivo src/redux/actions/index.js

import axios from 'axios';
import { API_LOGIN } from '../../config/const';

import {
    SET_SESSION
} from '../ActionTypes';

export const login = ({ email, password }) => (dispatch, getState) => {
    return new Promise((resolve, reject) => {
        axios.post(API_LOGIN,{
                email,
                password
            })
            .then((respJson) => {
                //si todo sale bien guardamos el user y token en localstore
                dispatch({
                    type: SET_SESSION,
                    user: respJson.data.user,
                    token: respJson.data.token
                })
                return resolve(respJson.data)
            })
            .catch( (err) => {
                if(err.response && err.response.data)
                    return reject(err.response.data)
                else
                    return reject({ error : true, message : "Ocurrio un error por favor intenta más tarde."});
            });
    });
}

Importamos axios que se va a encargar de hacer las peticiones a nuestra api, importamos la url para hacer el login e importamos los actionstypes para definir que accion debe realizar nuestro dispatch.

Tambien creamos la función login que recibe un objeto pero nosotros extramos solo el email y password ({ email, password }) si ocurre un error se ejecuta la funcion catch donde validamos que el end point respondio el error que ocurrio por ejemplo que el correo no existe o la contraseña no es correcta.

Pero si todo sale bien entonces se ejecuta la funcion then esto quiere decir que el endpoint valido contraseña e email y todo es correcto, ahora procedemos a ejecutar dispatch donde le pasamos el type que es un string que contiene el valor SET_SESSION, el user que es un objeto con el nombre de usuario, apellidos, etc… y el token que vamos a utilizar posteriormente.

¿Qué hace la función dispatch de redux?

Bueno esto es un poco complicado de explicar me costo un par de meses poder entender la logica de redux, pero al ejecutar la función dispatch y pasarle el type va a buscar en todos nuestros reducers que se encuntran en la carpeta /src/redux/reducers el case que contenga el mismo valor que SET_SESSION.

Esto quiere decir exactamente que va ejecutar el archivo src/redux/reducers/session.js y ejecutar el pequeño codigo que se encuentra en el switch.

case SET_SESSION : {
    const { token, user } = action
    return {
        token,
        user
    }
}

donde extraemos el objeto token y user  que le pasamos a la función dispatch y retornamos un objeto nuevo con estos valores, esto quiere decir que redux va actualizar o guardar los datos en el localstorage del telefono  en el key session la estructura seria algo asi:

{
    //objeto que contiene todos los reduxcer si nombres un reducer como post 
    // aqui se encontrara el key post con el valor por default que asinges en el reducers en el case default
    session : {
        user : {
            firstName : "nombre que respondio la api",
            lastName : "apellidos que respondio la api"
        },
        token : "Token que respondio la api"
    }
}

3.- Cómo conectar actions de redux en un component

Para poder conectar nuestro action en el componente debemos importar connect de react-redux y el action login

import { connect } from 'react-redux';
import { login } from '../redux/actions';

para usar connect y el componente lo usamos de la siguiente forma

export default connect(null,{  login })(Login);

De esta forma nuestro componente Login va a tener dentro de sus props todos los datos del store y el despachador de redux, aunque en este ejemplo solo estamos pasando el action login.

¿Qué hace la función connect de react-redux?

La función/decorador connect envuelve nuestro componente Login como primer parametro recibe todos los datos de nuestro Store que por el momento solo tenemos el reducers session un ejemplo de como utilizarlo seria el siguiente:

function MapStateToProps(state){
    return {
        user : state.session && state.session.user ? state.session.user : false
    }
}

export default connect(MapStateToProps,{  login })(Login);

Donde el primer parametro le pasamos el state que contiene todos los reducers con los datos, pero por cuestiones de redimiento solo queremos la data del usuario, en caso de que no exista hacemos una validación para que no cause un error y se detenga la app.

4.- Cómo ejecutar actions en nuestro componente

Ya  el action en nuestro componente ahora cuando el usuario presione el boton login debemos ejecutarlo.

import React, { Component } from 'react';
import { View,Alert } from 'react-native';
import Input from '../components/Input';
import Button from '../components/Button';
import LinearGradient from 'react-native-linear-gradient';
import {
    container
} from '../config/styles';

import { connect } from 'react-redux';
import { login } from '../redux/actions';

class Login extends Component{
    
    constructor(props){
        super(props);
        this.state = {
            email       : '',
            password  : '',
            loader       : false
        }
    }

    _getDisabled(){
        let disabled = false;
        if(!this.state.email)
            disabled = true;
        if(!this.state.password || this.state.password.length <=5)
            disabled = true;
        if(this.state.loader)
            disabled = true;
        return disabled;
    }


    _login(){
        this.setState({ lodaer : true })
        this.props.login(this.state).then(($result) => {
            //todo salio bien enviamos a otra vista donde veremos el perfild del usuario
        }).catch( (err) => {
            Alert.alert('Error',err.message);
        })
    }

    render(){
        return (
            <LinearGradient
                colors={['#441E35','#320622']}
                style={container}>

                <Input
                    placeholder="Correo electrónico"
                    onChangeText={(email) => this.setState({email})}
                    value={this.state.email}
                />
                <Input
                    secureTextEntry={true}
                    placeholder="Contraseña"
                    onChangeText={(password) => this.setState({password})}
                    value={this.state.password}
                />
                <Button
                    disabled={this._getDisabled()}
                    text={ (this.state.loader ? 'Cargando...' : 'Iniciar Sesión')}
                />
            </LinearGradient>
        )
    }
}

function MapStateToProps(state){
    return {
        user : state.session && state.session.user ? state.session.user : false
    }
}

export default connect(MapStateToProps,{  login })(Login);

Como pueden ver en el ejemplo si el usuario presiona el boton se ejecuta la función _login donde indicamos al state que esta cargando y asi deshabilitamos el boton para que el usuario no presione varias veces y por experiencia coloque un promise para saber cuando se termina la petición POST y  mostrar algun error o enviar al usuario a un nuevo stackNaviation donde  ya podemos hacer todas la funciones de un usuario logueado.

El resultado es el siguiente:

Vamos a mostrar la data que esta en redux en el componente Home usando la funcion connect de react-redux y pasando en los props el state session donde una vez que iniciamos sesión veremos los datos que guardamos.

import React, { Component } from 'react';
import { View, Text } from 'react-native';
import Button from '../components/Button';
import LinearGradient from 'react-native-linear-gradient';
import {
    container
} from '../config/styles';
import { connect } from 'react-redux';


class Home extends Component{
    
    constructor(props){
        super(props);
        this.state = { }
    }

    _go(routeName){
        this.props.navigation.navigate(routeName);
    }

    render(){
        return (
            <LinearGradient
                colors={['#441E35','#320622']}
                style={container}>
                <Button
                    onPress={() => this._go('registro')}
                    text="¡Registrate!"
                />
                <Button
                    onPress={() => this._go('login')}
                    text="¿Ya tienes una cuenta? Inica sesión"
                />
                <Text style={{ color : 'white'}}>{JSON.stringify(this.props.session)}</Text>
            </LinearGradient>
        )
    }
}

function MapStateToProps(state){
    return {
        session : state.session
    }
}
export default  connect(MapStateToProps)(Home);

el resultado es el siguiente:

 

Gracias por leer y compartir, les puedo recomendar los siguientes libros que me ayudaron mucho para de mi codigo mas optimo y usar buenas practicas al momento de programar.

 

 

Te puede interesar:

¿Como programar un api rest en SailsJs para iniciar sesión con jsonwebtokens?
¿Como programar una api en sailsJs para registrar usuarios con mongodb y jsonwebtokens?
¿Cómo configurar social meta tags para aplicaciones en AngularJs ó ReactJs con Nginx?
¿Cómo configurar notificaciones push en chrome con firebase?
Posted in: NodeJs, React, react native, Redux Tagged with: api rest, axios, axios post react, axios post requets, connect redux, jsonwebtokens, login, login con react, mongodb, react, react native, react redux, redux, token web, tokens
Copyright © 2021 Herel Odin