Purchasing list design with React Native and Expo .
git clone https://github.com/RafaelR4mos/challenge-react-native-shop-list.git package.json npm installnpm run startUse the icon library for the RN version.
npm install phosphor-react-nativeUse of icons:
import { Basket } from 'phosphor-react-native'
export function Componente() {
return (
<Basket
size={32}
color="#FFFFFF"
weight='bold'
/>
)
}
Stylized components and use of themes. The biggest difference for the web version is in the typing and noting for the RN version
//styles.ts
import 'styled-components/native' ;
export const Container = styled . View `
color: red;
` ; //tipagem
import 'styled-components/native' ;
import theme from '../theme' ; //caminho do tema
//importante usar o '/native' aqui também.
declare module 'styled-components/native' {
type ThemeType = typeof theme ;
export interface DefaultTheme extends ThemeType { }
} attrs Attributes So that it is possible to consume the theme of the application and not have to styling in the .tsx file itself we can use in styles.ts the following code:
//Aqui mudamos os atributos 'size' e 'color' através do arquivo de estilização
export const BackIcon = styled ( CaretLeft ) . attrs ( ( { theme } ) => ( {
size : 32 ,
color : theme . COLORS . WHITE ,
} ) ) `` ;type on TypeScriptIf we create a personalized component and it is also necessary to extend the typing of the native component, we can use the code as a base:
//Importa tipagem do touchable opacity que vem do RN
import { TouchableOpacityProps } from 'react-native' ;
//Com o caractere '&' adiciona os tipos nativos + o que for definido entre chaves
type GroupCardProps = TouchableOpacityProps & {
title : string ;
} ; When there is a use of many variables such as theme , type , variant and others in one component it may only be that the Styled-Components css Helper contributes to simplifying syntax.
import styled , { css } from 'styled-components/native' ;
//Com isso `theme` não precisa ser desestruturado em todas propriedades.
export const NumbersOfPlayers = styled . Text `
${ ( { theme } ) => css `
color: ${ theme . COLORS . GRAY_200 } ;
font-family: ${ theme . FONT_FAMILY . BOLD } ;
font-size: ${ theme . FONT_SIZE . SM } px;
` } ;
` ;npm install @react-navigation/nativeexpo projects npx expo install react-native-screens react-native-safe-area-contextnpm install @react-navigation/native-stackcontexto of routes and rotas : routes/app.routes.tsx
import { createNativeStackNavigator } from '@react-navigation/native-stack' ;
import { MyLists } from '../screens/MyyList' ;
const { Navigator , Screen } = createNativeStackNavigator ( ) ;
//Navigator --> Envolve as rotas
//Screen --> Rota individual com nome e apontando para componente
export function AppRoutes ( ) {
return (
< Navigator screenOptions = { { headerShown : false } } >
< Screen
name = "myLists"
component = { MyLists }
/>
</ Navigator >
) ;
} index.tsx
import { NavigationContainer } from '@react-navigation/native' ;
import { useTheme } from 'styled-components/native' ;
import { AppRoutes } from './app.routes' ;
import { View } from 'react-native' ;
export function Routes ( ) {
const { COLORS } = useTheme ( ) ;
//NavigationContainer --> Fornece o contexto de navegação para o app.
// * A estilização da view remove o glitch effect ao trocar de página.
return (
< View style = { { flex : 1 , backgroundColor : COLORS . GRAY_600 } } >
< NavigationContainer >
< AppRoutes />
</ NavigationContainer >
</ View >
) ;
} @types/ It is interesting to tell which routes exist in our application and especially which params are expected on each of the routes
Create a navigation.d.ts file
Rewrite the module typing in the file
export declare global {
namespace ReactNavigation {
interface RootParamList {
myLists : undefined ;
newList : undefined ;
list : {
listName : string ;
} ;
}
}
} At times we need to exchange information between pages of our application, for this, we can use LIB react-navigation AND HIS HOOKS
useNavigation() import { useNavigation } from '@react-navigation/native' ; const navigation = useNavigation ( ) ;navigate navigation . navigate ( 'route' , { state } ) ; It is important to use the Hook useRoute , which also comes from react-navigation . In this it is possible to disrupt the parameters from inside route.params PARAMS IDEIAL IS ALSO TIAR WHAT ARE THESE PARAMS WITH THE "AS" + TIPAGE
const route = useRoute ( ) ;
const { param } = route . params as RouteParams ; useFocusEffect for focus on the page useFocusEffect is quite similar to useEffect , however it is activated whenever the page receives focus, ie in addition to the 1st load it is invoked if a navigation occurs to the page
Import:
import { useFocusEffect } from '@react-navigation/native' ;Use:
useFocusEffect (
useCallback ( ( ) => {
fetchLists ( ) ;
} , [ ] )
) ;
useCallBackis used together to avoid firing unnecessary rendering, which can help with application performance.
Similar to the browser location. It can help solve prop-drilling problems as it centralizes information in one place.
Installation:
npx expo instal @react-native-async-storage/async-storage
The dynamics of dealing with AsyncStorage , similar to localStorage, but asynchronous is different and there is a pattern that can be used.
Creation of a folder only for this storage
Creation of a storageConfig.ts file to define Storage Keys
With this, we guarantee better maintenance in the keys of the elements saved in AsyncStorage
const LIST_COLLECTION = '@shop-list:lists' ;
const ITEM_COLLECTION = '@shop-list:items' ;
export { LIST_COLLECTION , ITEM_COLLECTION } ;Example: Player
Archives:
| -List | listcreate.ts | listdelete.ts | listgetall.ts | listgetsingle.ts
import AsyncStorage from '@react-native-async-storage/async-storage' ;
import { LIST_COLLECTION } from '../storageConfig' ;
import { ShoppingList } from '../../screens/Lists' ;
import { listsGetAll } from './listGetAll' ;
import { AppError } from '../../utils/AppError' ;
export async function listCreate ( newList : ShoppingList ) {
try {
const storedLists = await listsGetAll ( ) ;
const listAlreadyExists = storedLists
. map ( ( item : ShoppingList ) => item . title )
. includes ( newList . title ) ;
if ( listAlreadyExists ) {
throw new AppError ( 'Já existe uma lista com este nome.' ) ;
}
const newStorage = JSON . stringify ( [ ... storedLists , newList ] ) ;
await AsyncStorage . setItem ( LIST_COLLECTION , newStorage ) ;
console . log ( storedLists ) ;
} catch ( error ) {
throw error ;
}
}useRef() to deal with elements We can use the HOOK USEREF () to access the reference of an element , and thus deal with focus() , blur() , among others.
Example: By submitting a form we can use blur() in the input element, after all, the user has already entered what was needed, so the desfoque effect can end the open keyboard and remove the focus to the input.
//Criação da referência de vinculação
const newListNameInputRef = useRef < TextInput > ( null ) ;
function handleSubmit ( ) {
///...
//Desfoca o elemento
newListNameInputRef . current ?. blur ( ) ;
}
//IMPORTANTE adicionar o `ref` ao elemento, caso seja um componente é necessário enviar via prop.
return (
< View >
< Input
inputRef = { newListNameInputRef }
onChangeText = { setNewListName }
value = { newListName }
placeholder = "Nome da sua lista"
autoCorrect = { false }
onSubmitEditing = { handleAddList }
returnKeyType = "done"
/ >
< / View >
) ; So that we can distinguish a generic/unknown error provided by a throw and an error recognized by our application we can create a class with a message attribute and instantiate this class, so we can collect instaceof <classe> within the catch block.
Example:
export class AppError {
message : string ;
constructor ( message : string ) {
this . message = message ;
}
} throw new AppError ( 'Estas lista já esta adicionada!' ) ; catch ( error ) {
if ( error instanceof AppError ) {
Alert . alert ( 'Nova lista' , error . message ) ;
} else {
Alert . alert ( 'Nova lista' , 'Não foi possível adicionar' ) ;
console . error ( error ) ;
}
}By: Rafael Ramos?