Fala Samurai, blz?
Nesse artigo vamos introduzir uma ferramenta que permite adicionar tipagem e alguns recursos adicionais a linguagem JavaScript possibilitando melhorar a estrutura dos projetos em React Native e deixar o código escalável para facilitar a manutenção futura.
Quando iniciamos nossos projetos em React Native é comum seguir codificando sem preocupações como “qual o tipo da variável X” ou “oque a função Y vai retornar” ou se não vai retornar nada e só vai fazer alguma coisa, etc. E é natural, afinal, o Javascript permite que nossos programas sejam escritos com tipagem dinâmica, isso quer dizer que as variáveis e funções podem assumir tipos diferentes no decorrer da execução do código.
E isso pode parecer favorecer na produtividade, ou seja, na medida em que vamos escrevendo o código sem nos preocuparmos com a tipagem perdemos menos tempo.
Porém, existem situações em que essa possibilidade de escrever o código de forma dinâmica pode gerar outros problemas e essa aparência de produtividade acaba caindo por água abaixo e então vai valer a pena pensar em trabalhar com tipagem estática e é sobre isso que vamos falar nesse artigo ao introduzir o uso de Typescript com React Native.
Índice
O que é TypeScript e por que utilizar?
O TypeScript não é uma linguagem de programação e sim uma ferramenta criada pela Microsoft com o intuito de resolver alguns problemas relacionados a tipagem naturalmente dinâmica da linguagem JavaScript.
Imagine um projeto escrito em React Native em que você não mexe já faz algum tempo e precisa realizar uma manutenção ou ajuste no código.
Estando ele escrito com tipagem dinâmica, você irá precisar entender o que as funções estão retornando ou se só estão fazendo alguma coisa e de que tipo são as variáveis do código, com que valor elas iniciaram e depois se assumiram algum outro valor, etc.
Também pode acontecer que outra pessoa da equipe vá mexer no seu código e por algum motivo não tenha a mesma linha de raciocínio que você teve ao nomear as variáveis e pode pensar que a variável X que estava trabalhando com números agora possa receber um texto ou simplesmente faz isso porque foi mais fácil ali na hora de mexer e a linguagem permite.
No primeiro caso isso pode te custar mais tempo e no segundo caso pode causar erros no futuro ou confusão e uma estrutura ruim de código.
Nos dois casos temos um problema que pode ser resolvido com padronização de código e utilização do TypeScript.
Tipagem
Quando falamos de tipagem no TypeScript queremos dizer que ao declarar uma variável ou função definimos um tipo para ela e que ela vai assumir esse tipo durante todo o ciclo de vida em tempo de execução do código, ex de declaração:
let decimal: number = 6;
let color: string = "blue";
E vai acusar um erro ao tentar definir um valor diferente de
number
para a variável
decimal
ou um valor diferente de
string
para a variável
color
.
Os tipos possíveis são:
- Boolean;
- Number;
- String;
- Array;
- Tuple;
- Enum;
- Any;
- Void;
- Null and Undefined;
- Never;
- Object;
Usando TypeScript no React Native
Para usar o TypeScript com React Native você pode começar um novo projeto já pré configurado com o comando:
npx react-native init MyTSProject --template [email protected]
Ou pode utilizar em um projeto já existente fazendo algumas instalações e modificações necessárias.
Para o nosso exemplo, iremos utilizar um projeto já existente que pode ser baixado nesse repositório do github da Dev Samurai:
Link para o SamuraiCam no Github
Se quiser saber como criar esse projeto do zero, acesse nosso tutorial do SamuraiCam:
React Native Tutorials: Usando a Câmera do Celular
Bom, com o projeto em mãos, a primeira coisa a fazer depois de rodar o
yarn
é instalar as dependências necessárias para utilizar o Typescript:
yarn add typescript @types/jest @types/react @types/react-native @types/react-test-renderer
Depois precisamos criar 2 arquivos na raiz do projeto para configurar as dependências do TypeScript.
Um deles é tsconfig.json:
{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "react",
"lib": ["es6"],
"moduleResolution": "node",
"noEmit": true,
"strict": true,
"target": "esnext"
},
"exclude": [
"node_modules",
"babel.config.js",
"metro.config.js",
"jest.config.js"
]
}
O outro é jest.config.js:
module.exports = {
preset: “react-native”,
moduleFileExtensions: [“ts”, “tsx”, “js”, “jsx”, “json”, “node”]
};
Beleza, agora todo arquivo do projeto que desejamos utilizar o TypeScript precisa ser renomeado para a extensão
.tsx
e no nosso exemplo só temos o App.js, então vamos renomear para App.tsx.
Precisamos agora adaptar o código para que seja um arquivo TypeScript válido.
Se rodar o comando
yarn tsc
ele vai verificar os arquivos
.tsx
e irá apontar os erros encontrados.
Nosso código vai retornar 7 erros. No console você verá os erros encontrados, os locais dos arquivos e no final o total que deve estar assim Found 7 errors.
A ideia é corrigir esses erros para que o código fique padronizado com o TypeScript.
Os primeiros erros notados são em dois imports no começo do arquivo:
import Icon from 'react-native-vector-icons/MaterialIcons';
e
import logo from './assets/logo.png';
Dizendo que esses módulos não foram encontrados, então nós vamos criar um arquivo chamado declarations.d.ts na raiz do projeto em que podemos centralizar a declaração dos módulos necessários:
declare module 'react-native-vector-icons/MaterialIcons';
declare module '*.png';
Outro erro que aparece são com as
props
do componente
Camera
, pois, não temos uma tipagem definida para aquelas
props
.
E como temos 3 tipos de
props
, podemos criar uma
interface
para definir os tipos das propriedades recebidas pelo
Camera
.
No arquivo declarations.d.ts adicione:
interface CameraProps {
isVisible: boolean;
onChangePhoto(uri: string): void;
onCloseCamera(): void;
}
Ou seja, criamos uma
interface
chamada
CameraProps
que possuí 3 valores:
-
isVisible
é uma variável que deve receber um valor booleano; -
onChangePhoto
é uma função que deve receber sempre um parâmetrouri
do tipo string e ela não retorna nenhum valor (void
); -
onCloseCamera
é uma função que não recebe nenhum parâmetro e não retorna nada (void
);
Agora la no App.tsx vamos definir que as
props
de
Camera
são do tipo
CameraProps
:
const Camera = ({isVisible, onChangePhoto, onCloseCamera}: CameraProps) => {...}
Depois, no componente
App
temos a função
onChangePhoto
que recebe um parâmetro
newPhoto
e ele deve ser tipado como string, pois, definimos lá no
CameraProps
que o método
onChangePhoto
deve receber um parâmetro string e é essa função que estamos enviando nas
props
do componente
Camera
.
const [photo, setPhoto] = useState();
const onChangePhoto = (newPhoto: string) => {
setPhoto(newPhoto);
setIsCameraVisible(false);
};
No final nosso App.tsx deve estar assim:
import React, { useState } from "react";
import {
Alert,
Modal,
View,
Image,
ImageBackground,
TouchableOpacity,
StyleSheet
} from "react-native";
import Icon from "react-native-vector-icons/MaterialIcons";
import { RNCamera } from "react-native-camera";
import logo from "./assets/logo.png";
const Camera = ({ isVisible, onChangePhoto, onCloseCamera }: CameraProps) => {
const [camera, setCamera] = useState();
const onTakePicture = async () => {
try {
const { uri } = await camera.takePictureAsync({
quality: 0.5,
forceUpOrientation: true,
fixOrientation: true,
skipProcessing: true
});
onChangePhoto(uri);
} catch (error) {
Alert.alert("Erro", "Houve um erro ao tirar a foto.");
}
};
return (
<Modal animationType="slide" transparent={false} visible={isVisible}>
<RNCamera
ref={ref => setCamera(ref)}
style={{ flex: 1 }}
type={RNCamera.Constants.Type.back}
autoFocus={RNCamera.Constants.AutoFocus.on}
flashMode={RNCamera.Constants.FlashMode.off}
androidCameraPermissionOptions={{
title: "Permissão para usar a câmera",
message: "Precisamos da sua permissão para usar a câmera.",
buttonPositive: "Ok",
buttonNegative: "Cancelar"
}}
captureAudio={false}
>
<Icon
name="photo-camera"
size={40}
color={"#fff"}
onPress={onTakePicture}
style={styles.buttonTakePicture}
/>
<Icon
name="close"
size={50}
color={"#fff"}
onPress={onCloseCamera}
style={styles.buttonCloseCamera}
/>
</RNCamera>
</Modal>
);
};
const App = () => {
const [isCameraVisible, setIsCameraVisible] = useState(false);
const [photo, setPhoto] = useState();
const onChangePhoto = (newPhoto: string) => {
setPhoto(newPhoto);
setIsCameraVisible(false);
};
const onCloseCamera = () => {
setIsCameraVisible(false);
};
return (
<View style={styles.container}>
<Image source={logo} style={styles.logo} />
<View style={styles.photo}>
<ImageBackground
style={{ width: "100%", height: "100%" }}
source={{ uri: photo }}
/>
</View>
<View style={styles.buttons}>
<TouchableOpacity
style={styles.button}
onPress={() => {
setIsCameraVisible(true);
}}
>
<Icon name="camera-alt" size={40} color={"#f37272"} />
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => {
setPhoto(null);
}}
>
<Icon name="delete" size={40} color={"#f37272"} />
</TouchableOpacity>
</View>
<Camera
isVisible={isCameraVisible}
onChangePhoto={onChangePhoto}
onCloseCamera={onCloseCamera}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f37272"
},
logo: {
alignSelf: "center",
marginTop: 60
},
photo: {
width: 300,
height: 200,
backgroundColor: "#fff",
alignSelf: "center",
marginTop: 80
},
buttons: {
marginTop: 10,
flexDirection: "row",
justifyContent: "center"
},
button: {
backgroundColor: "#fff",
margin: 20,
borderRadius: 150,
width: 80,
height: 80,
alignItems: "center",
justifyContent: "center"
},
buttonTakePicture: {
flex: 0,
alignSelf: "center",
position: "absolute",
bottom: 20
},
buttonCloseCamera: {
flex: 0,
position: "absolute",
top: 20,
right: 20
}
});
export default App;
E ao rodar o comando
yarn tsc
novamente para verificar os erros a mensagem deve estar parecida com essa: Done in 4.28s.
Conclusão
Utilizar o TypeScript tem diversas vantagens que podem não parecer fazer sentido para quem não está acostumado, pois, tem uma média curva de aprendizado para entender as especificações e as formas corretas de se utilizar a tipagem.
Mas é interessante conhecer e utilizar, pois, em projetos que começam a crescer em escopo ou equipe, a padronização e alinhamento entre os devs acabada se tornando crucial e aplicar tipagem e os recursos de orientação a objetos oferecidos pelo TypeScript é uma excelente escolha.
Nesse artigo tivemos apenas uma introdução básica sobre o assunto mas pretendemos criar um projeto do zero em React Native utilizando TypeScript com as melhores práticas, por isso fica de olho nos anúncios da nossa comunidade la no Discord:
Comunidade Dev Samurai no Discord
O código de exemplo utilizado aqui veio do projeto SamuraiCam que está disponível para download nesse repositório:
Link do Github para o SamuraiCam
E também temos um tutorial bem legal, ensinado o passo a passo para criar esse projeto (SamuraiCam) do zero:
React Native Tutorials: Usando a Câmera do Celular
E para você não ficar de fora dos nossos artigos e tutoriais sobre programação, carreira e desenvolvimento de aplicativos, cadastre seu e-mail e receba os anúncios fresquinhos das novidades aqui do blog
Valeu Samurai por acompanhar esse artigo e até a próxima.