Os aplicativos React Native são construídos usando componentes que gerenciam o estado internamente. O gerenciamento de estado embutido do React Native funciona bem para aplicativos que têm poucos componentes e nenhum escopo para escalar no futuro. No entanto, para aplicativos com um grande número de componentes ou para aqueles com potencial para aumentar o escopo no futuro, o estado de compartilhamento entre os componentes torna-se complexo.

Bibliotecas de gerenciamento de estado como Redux existem para resolver este problema. Redux fornece uma localização central para todos os estados de uma aplicação onde cada componente pode acessar os valores armazenados no estado. O “lugar central” é chamado de loja na terminologia do Redux.

Neste tutorial, veremos como gerenciar o estado usando Redux dentro de um aplicativo React Native. Este artigo cobre: ​​

  • Tipos de ação e ações assíncronas
  • Definindo um redutor
  • Configurando uma loja Redux
  • Usando React Hooks para despachar ações dentro de um componente React Native

Para entender esses conceitos, vamos criar um aplicativo de exemplo em que uma lista de itens de filme é obtida do Banco de dados de filmes (API TMDB). O código-fonte deste projeto está disponível neste repositório GitHub . Um usuário de aplicativo pode adicionar como favorito qualquer item de filme da lista e armazená-lo no estado do aplicativo para visualizá-lo em uma tela separada.

<”Prérequisitos

Antes de começar, certifique-se de ter o seguinte instalado em um ambiente local:

Depois de fazer login em sua conta TMDB, obtenha a chave API indo para Configurações > seção API e procurando a chave na seção API Chave (autenticação v3) .

The Movie Database API Homep idade

Para demonstrar, vou usar um simulador iOS. Se você preferir usar um dispositivo Android ou um emulador, os trechos de código compartilhados nesta postagem serão executados da mesma forma.

Usando Redux com React Native

Primeiros passos

Para começar a criar um novo projeto React Native, use React Native CLI e navegue dentro do diretório do projeto. Instalaremos algumas dependências, incluindo react-native-vector-icons e react-navigation/bottom-tabs para implementar um navegador de guia inferior, então criaremos um Loja Redux.

Para instalar a biblioteca React Navigation, consulte estas instruções da documentação oficial. Essas dependências podem mudar com o tempo.

Para configurar react-native-vector-icons, siga estas instruções.

Depois de instalá-los, abra uma janela de terminal e execute os seguintes comandos:

 npx react-nativo init reduxExemplo
cd reduxExample
yarn add @ react-navigation/native @ react-navigation/bottom-tabs react-native-gestual-handler react-native-reanimated react-native-screens react-native-safe-area-context @ react-native-community/masked-view react-native-vector-icons axios [email protected] [email protected] [email protected]

Se você estiver desenvolvendo para iOS, precisará instalar os pods para concluir a vinculação dessas bibliotecas externas usando CocoaPods:

 npx pod-install ios

Crie guias e um navegador de guias

Após o processo de instalação, criaremos um navegador de guias que será exibido na parte inferior da tela. A primeira guia exibirá uma lista de filmes usando dados coletados do terminal da API TMDB. O segundo exibirá uma lista de filmes que o usuário do aplicativo salvou como favoritos.

Começaremos criando duas telas simuladas. Crie um novo diretório chamado telas/ e dentro dele crie dois novos arquivos:

  • Favorites.js
  • Movies.js

Adicione o snippet de código correspondente a cada arquivo:

//telas/Favoritos.js
import React from'react';
import {View, Text, StyleSheet} from'react-native';
Const Favoritos=()=> { Retorna (   Favoritos   );
};
estilos const=StyleSheet.create ({ container: { flex: 1, justifyContent:'center', alignItems:'center', },
});
exportar favoritos padrão; //telas/Movies.js
import React from'react';
import {View, Text, StyleSheet} from'react-native';
Const Movies=()=> { Retorna (   Filmes   );
};
estilos const=StyleSheet.create ({ container: { flex: 1, justifyContent:'center', alignItems:'center', },
});
exportar filmes padrão;

Em seguida, crie um diretório chamado navegação e crie um novo arquivo chamado RootNavigator.js dentro dele. Adicione o seguinte snippet de código para criar uma guia inferior:

 import React from'react';
importar {NavigationContainer} de'@ react-navigation/native';
importar {createBottomTabNavigator} de'@ react-navigation/bottom-tabs';
importar MaterialIcons de'react-native-vector-icons/MaterialIcons';
//importar telas
importar filmes de'../screens/Filmes';
importar favoritos de'../screens/Favorites';
const Tab=createBottomTabNavigator ();
const tabBarOptions={ showLabel: false, activeTintColor:'# 9381ff', estilo: { altura:'10%', },
};
const RootNavigator=()=> { Retorna (    (  ), }} />  (  ), }} />   );
};
exportar RootNavigator padrão;

Modifique o arquivo App.js para renderizar o RootNavigator :

 import React from'react';
importar RootNavigator de'./navigation/RootNavigator';
const App=()=> { return ;
};
exportar aplicativo padrão;

Crie o aplicativo

Depois que a configuração for concluída, construiremos o aplicativo e o executaremos em um simulador iOS. Para construir o aplicativo para iOS, execute o comando npx react-native run-ios . Para executá-lo em um emulador Android, execute npx react-native run-android .

Aqui está como o resultado em um simulador iOS é mostrado:

Aplicativo executado saída do simulador Ios

Adicione uma ação para buscar dados

Uma ação no Redux é um payload de informações que é despachado do aplicativo para a loja que dispara um evento. Quando acontece algo que está diretamente relacionado ao estado do aplicativo, isso pode ser descrito como uma alteração do estado do aplicativo.

O estado no Redux é representado por um objeto JavaScript que pode ser considerado um objeto somente leitura. Só pode ser alterado por meio de uma ação.

A primeira tarefa na construção do aplicativo de exemplo é buscar os dados da API. Em seguida, adicionamos esses dados ao estado do aplicativo, que podem ser usados ​​em uma tela de componente para exibir uma lista de itens de filme.

Uma ação é construída a partir de um objeto com duas propriedades:

  • Uma string simples que descreve o tipo de ação que você deseja disparar. Por exemplo, se você deseja obter todos os itens do filme, o tipo de ação aqui é GET_MOVIES
  • Uma carga útil que contém os dados ou informações

Comece criando um novo diretório chamado redux e crie um novo arquivo chamado actions.js dentro dele. Adicione o seguinte tipo de ação:

 export const GET_MOVIES='GET_MOVIES';

Um tipo de ação é definido usando uma variável const no trecho de código acima para manter as coisas sustentáveis. Em um cenário do mundo real, pode haver várias ações para acionar eventos diferentes em um aplicativo Redux.

O tipo de ação GET_MOVIES é um tipo de ação responsável por fazer uma solicitação HTTP para buscar os dados de um endpoint da API. Isso é feito definindo um criador de ação. No Redux, este é um termo para uma função que retorna um objeto de ação.

Uma solicitação HTTP é construída de um BASE_URL e a API_KEY . Para o aplicativo de exemplo, vamos buscar a lista de filmes populares. Adicione o seguinte código para construir um BASE_URL e adicione a chave de sua conta no lugar de API_KEY .

 const API_URL='https://api.themoviedb.org/3/movie/popular';
const API_KEY='';
const PARAMS='page=1';
const BASE_URL=`$ {API_URL}? api_key=$ {API_KEY} & $ {PARAMS}`;

Para buscar os dados da API, vamos usar o Axios. Possui uma API de métodos como get e put para fazer as solicitações HTTP. Importe os Axios na parte superior do arquivo e, em seguida, defina um criador de ação chamado getMovies .

 export const getMovies=()=> { tentar { retornar envio assíncrono=> { const res=espera axios.get (`$ {BASE_URL}`); if (res.data) { Despacho({ tipo: GET_MOVIES, carga útil: res.data, }); } senão { console.log ('Não foi possível buscar'); } }; } catch (erro) { //Adicionar lógica personalizada para lidar com erros }
};

Adicionar um redutor de filmes

As ações por si só não podem atualizar o estado do aplicativo. Quando um estado muda, ele é tratado por uma função pura chamada redutor que calcula o estado atualizado do aplicativo com base no estado inicial ou atual. Como os redutores são funções puras, eles sempre produzem a mesma saída se o estado permanecer inalterado.

O redutor leva dois argumentos: o estado atual e uma ação. A sintaxe geral da função redutora pode ser descrita como:

 função Redutor (currentState, ação) { return newState;
}

Crie um novo arquivo chamado reducers.js . Importe a ação GET_MOVIES do arquivo actions.js . Agora, defina um objeto de estado inicial com duas matrizes vazias. A primeira matriz representa os itens do filme buscados no endpoint da API, e a segunda matriz armazena itens que o usuário do aplicativo escolheu como favorito.

A seguir, defina uma função moviesReducer com os argumentos initialState e action . Ele usa uma instrução switch para alternar entre os diferentes tipos de ação. Cada tipo de ação é definido por seu próprio caso . Atualmente, existe apenas um tipo de ação, que é buscar uma lista de filmes. Se o estado permanecer inalterado, o caso default retorna o estado atual.

 importar {GET_MOVIES} de'./actions';
const initialState={ filmes: [], favoritos: [],
};
function moviesReducer (state=initialState, action) { switch (action.type) { case GET_MOVIES: return {... estado, filmes: action.payload}; padrão: estado de retorno; }
}
exportar moviesReducer padrão;

Crie uma loja

Uma loja é um objeto que mantém o estado do aplicativo em nível global, em vez de em componentes individuais. É definido por uma função chamada createStore que leva o rootReducer como o primeiro argumento.

Um rootReducer é um objeto de todos os redutores. Um aplicativo cujo estado é gerenciado pelo Redux pode ter mais de um redutor. A biblioteca Redux oferece uma função especial chamada combineReducers para combinar todos os redutores dentro de um objeto.

Ao criar uma loja, é importante configurar a biblioteca redux-thunk , que dá acesso a um middleware chamado thunk . Ele permite que um repositório Redux faça solicitações AJAX assíncronas, como buscar dados de um endpoint de API. Por padrão, a natureza de qualquer ação despachada usando Redux é síncrona.

Uma função de middleware é passada como o segundo argumento para createStore . Para usar um middleware, Redux fornece uma função chamada applyMiddleware . Cada função de middleware é passada como um argumento para esta função.

Crie um novo arquivo chamado store.js e copie o seguinte snippet de código:

 import {createStore, combineReducers, applyMiddleware} de'redux';
importar conversão de'redux-thunk';
importar moviesReducer de'./reducers';
const rootReducer=combineReducers ({ moviesReducer,
});
exportar const store=createStore (rootReducer, applyMiddleware (thunk));

Para usar esta loja para gerenciar o estado do aplicativo React Native, importe-o dentro do arquivo App.js . Importe também o componente Provider da biblioteca react-redux . Ele envolve o componente raiz do aplicativo React Native e passa da loja para o restante do aplicativo.

 import React from'react';
import {Provider} from'react-redux';
import {store} from'./redux/store';
importar RootNavigator de'./navigation/RootNavigator';
const App=()=> { Retorna (    );
};
exportar aplicativo padrão;

Agora, qualquer componente React Native que faça parte do RootNavigator pode acessar o estado do aplicativo.

Exibir uma lista de filmes

Para exibir uma lista de filmes buscados no endpoint da API, vamos exibi-la dentro do componente de tela Movies.js .

Comece importando as seguintes declarações:

 import React, {useEffect} de'react';
import {View, Text, FlatList, Image, TouchableOpacity} de'react-native';
import {useSelector, useDispatch} de'react-redux';
importar MaterialIcons de'react-native-vector-icons/MaterialIcons';
import {getMovies} de'../redux/actions';

No snippet de código acima, usamos o gancho useSelector do Redux para acessar a matriz de filmes a partir do estado global do aplicativo.

Isso é semelhante à sintaxe mais antiga do argumento mapStateToProps , passado dentro do HOC connect () . A nova sintaxe permite que você extraia dados do estado Redux usando uma função de seletor.

Use o gancho useDispatch para despachar a ação getMovies para obter itens de filme da loja Redux. Modifique o componente Filmes conforme abaixo:

 exportar a função padrão BooksList () { const {movies}=useSelector (state=> state.moviesReducer); const dispatch=useDispatch (); const fetchMovies=()=> despacho (getMovies ()); useEffect (()=> { fetchMovies (); }, []); //...
}

Para renderizar os itens do filme, usaremos o componente FlatList do React Native. Ele aceita o array movies como o valor do prop data , enquanto o prop renderItem é usado para exibir cada item do filme do array.

Cada item do filme tem uma imagem de pôster que é exibida usando um componente Imagem e algumas meta informações na forma de título do filme e contagem de votos. Usando o componente TouchableOpacity , um botão “adicionar aos favoritos” é criado. Definiremos a lógica para atualizar o estado do redutor de filmes e exibi-los na guia “Favoritos” posteriormente.

 return (   Filmes populares    item.id.toString ()} renderItem={({item})=> { const IMAGE_URL= 'https://image.tmdb.org/t/p/w185'+ item.poster_path; Retorna (       {título do item}      {item.vote_count}   console.log ('Adicionado!')} activeOpacity={0,7} style={{ marginLeft: 14, flexDirection:'row', preenchimento: 2, borderRadius: 20, alignItems:'center', justifyContent:'center', altura: 40, largura: 40, }}>       ); }} showsVerticalScrollIndicator={false} />  
);

Aqui está o resultado que você obterá após esta etapa:

Saída de imagem de pôster React componente Flatlist nativo

Crie ações para adicionar e remover favoritos

No arquivo actions.js , vamos criar mais dois tipos de ação e seus criadores. Ambos os tipos de ação permitirão que um usuário do aplicativo adicione ou remova um item de filme da lista de favoritos.

 exportar const ADD_FAVORITE_ITEM='ADD_FAVORITE_ITEM';
exportar const REMOVE_FAVORITE_ITEM='REMOVE_FAVORITE_ITEM';

A seguir, defina os criadores de ação para cada um dos tipos de ação acima.

 export const addFavorite=movie=> dispatch=> { Despacho({ tipo: ADD_FAVORITE_ITEM, carga útil: filme, });
};
export const removeFavorite=movie=> dispatch=> { Despacho({ tipo: REMOVE_FAVORITE_ITEM, carga útil: filme, });
};

Agora, atualize moviesReducer no arquivo reducers.js .

 importar {GET_MOVIES, ADD_FAVORITE_ITEM, REMOVE_FAVORITE_ITEM} de'./actions';
const initialState={ filmes: [], favoritos: [],
};
function moviesReducer (state=initialState, action) { switch (action.type) { case GET_MOVIES: return {... estado, filmes: action.payload}; caso ADD_FAVORITE_ITEM: return {... estado, favoritos: [... state.favorites, action.payload]}; caso REMOVE_FAVORITE_ITEM: Retorna { ...Estado, favoritos: state.favorites.filter ( filme=> movie.id!==action.payload.id, ), }; padrão: estado de retorno; }
}
exportar moviesReducer padrão;

No arquivo Movies.js , cada item do filme possui um botão que alterna entre adicionar o item aos favoritos ou removê-lo. Vamos importar os dois criadores de ação:

//Atualize a seguinte linha
import {getMovies, addFavorite, removeFavorite} de'../redux/actions';

Atualize a seguinte linha para acessar o estado favoritos :

 const {filmes, favoritos}=useSelector (state=> state.moviesReducer);

Despache as seguintes ações para adicionar ou remover um item da lista de favoritos. A lista de favoritos aqui é representada pela matriz favoritos no estado:

//após a função de envio de busca de filmes, adicione:
const addToFavorites=filme=> despacho (addFavorite (filme));
const removeFromFavorites=filme=> despacho (removeFavorite (filme));
const handleAddFavorite=movie=> { addToFavorites (filme);
};
const handleRemoveFavorite=movie=> { removeFromFavorites (filme);
};

Vamos criar outro método de manipulador para alterar dinamicamente a IU com base nas ações acionadas acima. Este método verificará se um item de filme existe na matriz favoritos e acionará a alteração da IU com base no valor. A alteração da IU que queremos fazer é usar um botão favorito sólido quando um usuário o adiciona à lista de favoritos em vez de um esboço.

 const existe=filme=> { if (favoritos.filter (item=> item.id===filme.id).length> 0) { return true; } retorna falso;
};

Modifique o botão TouchableOpacity :

  existe (item)? handleRemoveFavorite (item): handleAddFavorite (item) } //outros adereços permanecem os mesmos
> 

Exibir favoritos

Qualquer item de filme adicionado à lista de itens favoritos também será exibido na guia Favoritos . Para implementar isso, abra o arquivo Favorites.js e comece importando as seguintes instruções:

 import React from'react';
import {Text, View, FlatList, TouchableOpacity, Image} de'react-native';
import {useSelector, useDispatch} de'react-redux';
importar MaterialIcons de'react-native-vector-icons/MaterialIcons';
import {removeFavorite} de'../redux/actions';

Como a tela Favoritos só mostrará a lista de itens adicionados, vamos buscar a matriz favoritos do estado usando o useSelector gancho.

Usar o gancho useDispatch irá disparar a ação para remover um item da lista. Depois que um item é removido da lista de favoritos, ele não é mais mostrado nesta tela específica.

Quando não há itens a serem exibidos ou, em outros termos, quando a matriz favoritos do estado está vazia, podemos exibir uma mensagem de espaço reservado.

Adicione o seguinte snippet de código ao componente Favoritos :

 const Favoritos=()=> { const {favoritos}=useSelector (estado=> estado.moviesReducer); const dispatch=useDispatch (); const removeFromFavorites=filme=> despacho (removeFavorite (filme)); const handleRemoveFavorite=movie=> { removeFromFavorites (filme); }; Retorna (   Favoritos   {favoritos.length===0? (  Adicione um filme à lista.  ): (  item.id.toString ()} showsVerticalScrollIndicator={false} renderItem={({item})=> { const IMAGE_URL= 'https://image.tmdb.org/t/p/w185'+ item.poster_path; Retorna (       {título do item}      {item.vote_count}   handleRemoveFavorite (item)} activeOpacity={0,7} style={{ marginLeft: 14, flexDirection:'row', preenchimento: 2, borderRadius: 20, alignItems:'center', justifyContent:'center', altura: 40, largura: 40, }}>       ); }} /> )}   );
};
exportar favoritos padrão;

Os componentes da IU são iguais aos da tela Filmes . Aqui está o resultado final após esta etapa:

Exibição do componente Favoritos

Conclusão

Usar o mecanismo de armazenamento no Redux para salvar todo o estado do seu aplicativo em um objeto global pode levar a problemas de desempenho ao usar muitos componentes. Dito isso, não há uma solução universal no mundo React Native para resolver todos esses problemas. Tudo se resume a qual estado um componente deve manter local e quais aspectos do estado do aplicativo devem ser compartilhados entre os componentes. O uso de ganchos com Redux torna simples de entender e usar o estado em um componente de função.

Para saber mais sobre Redux avançado, verifique a documentação oficial aqui .

A postagem Guia abrangente para usar Redux em React Native apareceu primeiro no LogRocket Blog .

Source link