Code Monkey home page Code Monkey logo

context-api-react-native's Introduction

Autenticação React Native utilizando o Context API


Iniciar projeto:

  • Executar na linha de comando:
npx react-native init NOME_PROJETO

ESLint, Prettier, Editor Config

  • Gerar o arquivo editorconfig:
root = true

[*]
end_of_line = lf
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
  • Antes de instalar o eslint, remova o arquivo .eslintrc.js e o arquivo .prettier

  • Executar no terminal:

yarn add eslint -D
  • Após instalado executar:
yarn eslint --init
  • Responder as perguntas:

  • How would you like to use ESLint?

  • To check syntax, find problems, and enforce code style

  • What type of modules does your project use?

  • JavaScript modules (import/export)

  • Which framework does your project use?

  • React

  • Where does your code run?

  • Remover todos

  • How would you like to define a style for your project?

  • Use a popular style guide

  • Which style guide do you want to follow?

  • Airbnb: https://github.com/airbnb/javascript

  • What format do you want your config file to be in?

  • JavaScript

  • Would you like to install them now with npm?

  • Y

  • Remover o arquivo package-look.json

  • Executar o comando yarn

  • Instalar mais algumas extensões para trabalhar com o ESLint:

  • Executar o seguinte no terminal:

yarn add prettier eslint-config-prettier eslint-plugin-prettier babel-eslint -D
  • No arquivo .eslintrc.js alterar o conteúdo para:
module.exports = {
  env: {
    es6: true,
  },
  extends: [
    'airbnb',
    'prettier',
    'prettier/react',
  ],
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
  },
  parser: 'babel-eslint',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  plugins: [
    'react',
    'prettier'
  ],
  rules: {
    'prettier/prettier': 'error',
    'react/jsx-filename-extension': [
      'warn',
      {
        extensions: ['.jsx', 'js']
      }
    ],
    'import/prefer-default-export': 'off'
  },
};

Nos arquivos a parte dos styles sempre é melhor vir antes que o component

  • Criar o arquivo .prettierrc com o seguinte conteudo:
{
  "singleQuote": true,
  "trailingComma": "es5"
}
  • Provavelmente após todos esse processos irá aparecer um monte de erros na tela do dispositivo, para corrigir, encerre o processo no terminal e execute o comando:
yarn start --reset-cache
  • Dessa forma irá abrir o bundle porém irá resetar as dependencias

  • E sempre que houver problemas sem explicação só executar esse comando ou executar o yarn PLATAFORMA


Organizar estrutura de pastas

  • Criar a pasta src e adicionar um arquivo index.js e adicionar o código que está no App.js e remover o arquivo App.js e no arquivo index.js que está na raiz importar o App de src/index.js:
import App from './src';

Root Import

  • Facilita a importação dos arquivos que estão dentro de várias pastas, ao ínves de utilizar ../../../pasta/arquivo ficará assim ~/pasta/arquivo;

  • Primeiro adicione a dependencia:

yarn add babel-plugin-root-import eslint-import-resolver-babel-plugin-root-import -D
  • Ajustar o arquivo babel.config.js com o seguinte conteúdo:
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    [
      'babel-plugin-root-import',
      {
        paths: [
          {
            rootPathSuffix: './src/',
            rootPathPrefix: '~/',
          },
        ],
      },
    ],
  ],
};
  • Na chave rootPathSuffix informamos a pasta que será utilizada no caso ./src/

  • No arquivo .eslintrc.js adicionamos o seguinte dentro de modules.exports:

settings: {
    "import/resolver": {
      "babel-plugin-root-import": {
        "rootPathPrefix": "~",
        "rootPathSuffix": "src"
      }
    }
  }
  • Estamos dessa forma informando que a pasta root do projeto será src

  • Crie também na raiz do projeto um arquivo jsconfig.json para o vscode não se perder nas importações com o seguinte conteúdo:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "~/*": ["src/*"]
    }
  }
}
  • Agora poderá utilizar ~/ ao invés de ../../

  • Para surtir efeito no app é necessário realizar a seguinte comando:

react-native start --reset-cache
  • Pois nem sempre fechando o emulador e e dando run irá funcionar

Navegação

No React Native a parte de navegação é diferente da parte de navegação do React da web

  • Criar as paginas: src/pages/Dashboard/index.js, src/pages/SignIn/index.js e src/pages/Loading/index.js

  • Criar o arquivo para rotas src/routes.js

  • Instalar a navegação:

yarn add @react-navigation/native
  • A dependencia do react-navigation muda constantemente por isso é sempre bom dar uma conferida na documentação para verificar as dependencias a serem instaladas.

  • Docs: React Navigation

  • é bom verificar o que ele pede, no caso nesse projeto foi instalado também:

yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
  • E depois precisei acessar a pasta ios/ e executar o comando:

Para mac OS instalar o cocoapods: CocoaPods

pod install
  • Para surtir efeitos nessas alterações é necessário executar novamente o comando:
yarn PLATAFORMA
yarn add @react-navigation/stack
  • No arquivo src/routes.js há um exemplo de como foi utilizado o Stack Navigator seguindo algumas opções de createStackNavigator

Importante: Para não quebrar a aplicação em produção é necessário adicionar a importação no arquivo de rotas:

import 'react-native-gesture-handler';

Styled Components

  • Instalação:
yarn add styled-components
  • Um exemplo pode ser visto em src/pages/SignIn/styles.js

  • No caso só pode ser utilizado components do Native, e ainda é necessário estilizar component por component não pode ser encadeado.

  • A vantagem é que consigo utilizar o css normal como na web.


Services

  • Inicialmente vamos criar a pasta src/services e vamos adicionar o arquivo auth.js, para simular uma chamada de API:
export function signIn() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        token: 'asfjaksldfjowerj2309r0923432oklj5l3j5k43j5kl32',
        user: {
          name: 'Rodolfo',
          email: '[email protected]',
        },
      });
    }, 2000);
  });
}
  • Vamos adicionar a dependência axios:
yarn add axios
  • E vamos criar o arquivo src/services/api.js, e colocar uma baseURL apenas para simular a chamada de api:
import axios from 'axios';

const api = axios.create({
  baseURL: 'http://localhost:3000',
});

export default api;

Context

  • Inicialmente vamos instalar a dependência @react-native-community/async-storage:
yarn add @react-native-community/async-storage
  • Acessar a pasta ios/ e executar o comando:
pod install
  • Inicialmente vamos criar o arquivo src/contexts/auth.js
// O que há de diferente é o `createContext` e `useContext`
import React, { createContext, useContext, useState, useEffect } from 'react';

import PropTypes from 'prop-types'; // Apenas para definir os tipos de parametros que serão passados para o component AuthProvider

// Semelhante ao localstorage porém para o react-native precisa disso.
import AsyncStorage from '@react-native-community/async-storage';

// Importamos as APIs de testes...
import * as auth from '~/services/auth';
import api from '~/services/api';


// Informamos a estrutura do objeto quais parametros que ele irá prover
const AuthContext = createContext({
  signed: false,
  user: {},
  loading: true,
  signIn: () => {},
  signOut: () => {},
});


// Será como component que será inserido em `src/App.js` e irá receber tudo que estiver embrulhado nesse componente em `src/App.js`
export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);

  const [loading, setLoading] = useState(true);

  // Ao montar o componente vamos obter os dados do AsyncStorage para verificar se o usuário está logado ou não.
  useEffect(() => {
    async function loadStorageData() {
      const storageUser = await AsyncStorage.getItem('@RNAuth:user');
      const storageToken = await AsyncStorage.getItem('@RNAuth:token');

      await new Promise((resolve) => setTimeout(resolve, 2000));

      if (storageUser && storageToken) {
        setUser(JSON.parse(storageUser));
        api.defaults.headers.Authorization = `Bearer ${storageToken}`;
      }
      setLoading(false);
    }
    loadStorageData();
  }, []);

  async function signIn() {
    const response = await auth.signIn();

    setUser(response.user);
    await AsyncStorage.setItem('@RNAuth:user', JSON.stringify(response.user));
    await AsyncStorage.setItem('@RNAuth:token', response.token);
    api.defaults.headers.Authorization = `Bearer ${response.token}`;
  }

  function signOut() {
    AsyncStorage.clear().then(() => {
      setUser(null);
    });
  }

  return (
    // aqui será disponibilizado as props que será disponibilizada para os demais componentes conforme o `AuthContext.createContext({})`
    <AuthContext.Provider
      value={{ signed: !!user, user, loading, signIn, signOut }}
    >
    { /* Aqui será recebido os elementos inseridos no `src/App.js` dentro de `AuthProvider` */ }
      {children}
    </AuthContext.Provider>
  );
};

AuthProvider.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.element,
    PropTypes.any,
  ]).isRequired,
};


// Hock para chamar o useContext mais facilmente.
export function useAuth() {
  const context = useContext(AuthContext);
  return context;
}

  • Vamos fragmentar o código acima para melhor entendimento:
import React, { createContext, useState } from 'react';

import * as auth from '~/services/auth';

const AuthContext = createContext({
  signed: false,
  user: {},
  signIn: () => {}, // será chamado para preencher a variavel `user`
  signOut: () => {}, // será chamado para limpar a variavel `user`
});

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);

  async function signIn() {
    const response = await auth.signIn(); // irá retornar: {token: 'asfjaksldfjowerj2309r0923432oklj5l3j5k43j5kl32',user: {name: 'Rodolfo',email: '[email protected]',},}

    setUser(response.user);
  }

  function signOut() {
    setUser(null);
  }

  return (
    // Será utilizado em `src/App.js`
    <AuthContext.Provider
      value={{ signed: !!user, user, loading, signIn, signOut }}
    >
      { /* Conteúdo desse componente utilizado no src/App.js */ }
      {children}
    </AuthContext.Provider>
  );
};
  • Criamos as rotas: src/routes/app.routes.js e src/routes/auth.routes.js acesse os arquivos para verificar o conteúdo.

  • Próximo passo no arquivo src/App.js:

import { AuthProvider } from '~/contexts/auth';

//..

<AuthProvider>
  <Routes />
</AuthProvider>

// ...
  • Como utilizar o Context API em um componente:
import React, { useContext } from 'react'; // Utilize o useContext
import { Button, Text } from 'react-native';
import { AuthContext } from '~/contexts/auth'; // Obtenha o context

export default function Component() {
  const { user, signIn } = useContext(AuthContext); // Obtenha as props desejada do context que foi passado através do `<AuthContext.Provider value={{ signed: !!user, user, loading, signIn, signOut }} >`

  function handleClick() {
    // Chamamos a function do context
    signIn();
  }

  use
  return (
    <>
      <Text>{user.name}</Text>
      <Button onPress={handleClick} title="Sign In" />
    </>
  );

  // ...
}
  • Importante que o component esteja de alguma form dentro de <AuthProvider> no caso desse projeto estamos colocando dentro das rotas

AsyncStorage

  • Para armazenar os dados de login para recuperar após fechar e abrir o app, utilizamos o AsyncStorage
yarn add @react-native-community/async-storage
  • Acessar a pasta ios/ e executar o comando:
pod install
  • Isso ficará sobre a responsabilidade do Context src/contexts/auth.js

  • Ao realizar o signIn():

async function signIn() {
  const response = await auth.signIn();
  setUser(response.user);

  // Não podemos salvar objeto javascript diretamente, convertemos para string.
  await AsyncStorage.setItem('@RNAuth:user', JSON.stringify(response.user));
  await AsyncStorage.setItem('@RNAuth:token', response.token);
}
  • Ao acessar o app verificamos se o usuário está com os dados salvos:
useEffect(() => {
  async function loadStorageData() {
    const storageUser = await AsyncStorage.getItem('@RNAuth:user');
    const storageToken = await AsyncStorage.getItem('@RNAuth:token');

    await new Promise((resolve) => setTimeout(resolve, 2000));

    if (storageUser && storageToken) {
      setUser(JSON.parse(storageUser));
    }
    setLoading(false);
  }
  loadStorageData();
}, []);

axios

  • Instale o axios:
yarn add axios
  • crie o arquivo src/services/api.js

  • Utilizar token após obté-lo, então podemos definir na api axios:

import api from '~/services/api';

// ...

async function signIn() {
  // ...

  api.defaults.headers.Authorization = `Bearer ${response.token}`;
}
  • E quando obtemos quando abrimos o app e verificamos se as credenciais estão salvas:
import api from '~/services/api';

// ...

if (storageUser && storageToken) {
  setUser(JSON.parse(storageUser));
  api.defaults.headers.Authorization = `Bearer ${storageToken}`;
}

// ...
  • Mais detalhes abra o arquivo src/contexts/auth.js

Loading

  • O AsyncStorage é async então a tela Sign In será exibida e logo depois a Dashboard dará uma piscada de uma tela para outra. Para tratar isso:

    • criamos uma view de Loading src/pages/Loading/index.js, caso deseje pode utilizar o React Native Splash Screen se preferir.

    • No src/contexts/auth.js adicionamos uma prop loading:

    const AuthContext = createContext({
      // ...
      loading: true,
      //...
    });
    • Ainda nesse arquivo criamos um state para controlar o loading:
    // ...
    const [loading, setLoading] = useState(true);
    // ...
    
    useEffect(() => {
      // ...
      setLoading(false);
      // ...
    }, []);
    // ...
    
    //adicionamos o `loading` no value do component `AuthContext.Provider`
    
    <AuthContext.Provider
        value={{ signed: !!user, user, loading, signIn, signOut }}
      >
      {children}
    </AuthContext.Provider>
    // ...
    • Finalmente em src/routes/index.js realizamos a condição:
    import React from 'react';
    // ...
    import { useAuth } from '~/contexts/auth';
    import Loading from '~/pages/Loading';
    
    const Routes = () => {
      const { signed, loading } = useAuth();
    
      if (loading) {
        return <Loading />;
      }
    
      // ...
    };
    
    export default Routes;

hock para o Context API

  • Para não chamar o useContext e o context nos componentes podemos gerar um hock no própiro context.

  • No arquivo src/contexts/auth.js adicionamos:

// Adicionado o `useContext`
import React, { createContext, useState, useEffect, useContext } from 'react';

// ...


// Esse será o hock que será chamado nos componentes
export function useAuth() {
  const context = useContext(AuthContext);
  return context;
}
  • Então nos componentes podemos chamar apenas:
// ...
import { useAuth } from '~/contexts/auth';

const Component = () => {
  const { signOut, user } = useAuth();
  // ...
}
  • Por fim para utilizarmos mais de um Context API podemos empilhar um dentro do outro no caso no src/App.js:
<Context1>
  <Context2>
  </Context2>
</Context1>

context-api-react-native's People

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.