Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 17, 2022 08:56 pm GMT

Como desacoplar a Navegao no React Native

Quando comeamos a desenvolver uma aplicao em React Native, umas das coisas primordiais que todo aplicativo tem a navegao entre telas. Bem, comum pensarmos que quando falamos em navegao no React Native, j associamos ao React Navigation e j samos instalando as bibliotecas e criando a estrutura de navegao. At ai tudo bem, mas o meu ponto aqui , quando comeamos a injetar diretamente as aes de navegao no nosso Presenter.

Esse questionamento se veio de um incmodo pessoal e dvida, se existem outras bibliotecas que realizam a navegao no React Native, e sim, existem algumas como React Native Navigation by Wix, React Router Native etc.

Ento, e se um dia eu desejar utilizar outra biblioteca de navegao sem ser a do React Navigation, o quanto de esforo eu teria pra fazer isso, considerando que as aes de navegao esto acopladas diretamente no meu Presenter?

Para isso, decidi realizar esse experimento em um projeto pessoal utilizando conceitos como TDD e Clean Architecture, onde voc pode encontr-lo aqui. E aproveitei para escrever este artigo.

Esse artigo est divido nas seguintes etapas:

1 . Realmente faz sentido desacoplar a Navegao no React Native?

2 . Montando a estrutura inicial de Navegao com React Navigation

3 . Utilizando Camadas para desacoplar o React Navigation da minha Aplicao

4 . Chamando a Ao de Navigate no nosso Presenter

5 . Escrevendo alguns cenrios de Testes Unitrios

Realmente faz sentido desacoplar a Navegao no React Native?

Quando pensamos no trabalho a mais que isso pode trazer no nosso desenvolvimento, pode no fazer sentido desacoplar as aes de navegao do nosso Presenter. Afinal pode existir algum cenrio onde eu abra mo do React Navigation para usar outra biblioteca?

Nesse caso, eu acredito que vai depender da deciso do dev, ou do time em conjunto.

Mas se isso um dia acontecer, imagine o trabalho que ia dar trocar em todos os Presenter's a ao de navegar por exemplo .

Abaixo podemos ver uma comparao utilizando uma Interface de navegao via props, e a forma comum que utilizar a navegao direta do React Navigation.

type Props = {  navigate: Navigate;};const WelcomePresenter: React.FC<Props> = ({ navigate }) => {  const buttonAction = () => {    navigate.navigateToMyPlans();  };}
const WelcomePresenter: React.FC<Props> = ({ navigation }) => {  const buttonAction = () => {    navigation.navigate(Routes.ACTIVITY);  };}

Aparentemente a diferena quase nula, mas se pararmos para pensar, quando utilizamos o navigation diretamente, criamos uma dependncia da navegao que estamos usando.

Montando a estrutura inicial de Navegao com React Navigation

Ento vamos l, precisamos primeiro deixar o nosso React Navigation configurado. Considerando que voc j saiba instalar as bibliotecas necessrias do React Navigation, irei apenas deixar o link para documentao em casos de dvida.

Primeiro vamos montar o nosso Container de Navegao:

type Props = {  setNavigationTop: (navigatorRef: NavigationContainerRef<any>) => void;  initialRouteName: keyof StackParams;};const Navigation: React.FC<Props> = ({  setNavigationTop,  initialRouteName,}) => {  return (    <NavigationContainer ref={setNavigationTop} theme={DefaultThemes}>      <StackNavigation initialRouteName={initialRouteName} />    </NavigationContainer>  );};export default Navigation;

No projeto eu utilizei o tipo de navegao em pilha, ento agora vamos configurar a navegao em pilha das nossas telas:

const Stack = createNativeStackNavigator<StackParams>();type StackNavigationParams = {  initialRouteName: keyof StackParams;};const StackNavigation: React.FC<StackNavigationParams> = ({  initialRouteName,}) => {  return (    <Stack.Navigator      initialRouteName={initialRouteName}      screenOptions={{        headerTransparent: true,        headerBackTitleVisible: false,        headerTintColor: colors.white,        title: '',      }}    >      <Stack.Screen name={Routes.WELCOME}>        {(props) => <WelcomeFactory {...props} />}      </Stack.Screen>    </Stack.Navigator>  );};export default StackNavigation;

O que falta agora s adicionarmos o nosso Navigation na raiz da aplicao, ento ficaria mais ou menos assim:

type Props = {  initialRouteName: keyof StackParams;};const Main: React.FC<Props> = ({ initialRouteName }) => {  return (    <WrapperScreen>      <StatusBar barStyle="dark-content" />      <Navigation        setNavigationTop={(navigationRef: NavigationContainerRef<any>) =>          setTopLevelNavigator(navigationRef)        }        initialRouteName={initialRouteName}      />    </WrapperScreen>  );};

Legal, essa seria a configurao necessria para termos em nossa aplicao a navegao entre telas. Obviamente no detalhei tanto as importaes e os tipos utilizados, at porque no objetivo deste artigo. Mas se quiser ver os detalhes, voc pode encontrar nesse repositrio.

Utilizando Camadas para desacoplar o React Navigation da minha Aplicao

Ento finalmente chegamos na cereja do bolo, aqui vamos quebrar um pouco o rito, e vamos utilizar alguns conceitos como Camadas, Interfaces, Adapters etc.

Para representar esse desacoplamento da ao de navegao, vamos seguir esse diagrama que demonstra as camadas que vamos utilizar.

Diagram of Navigate

Mas antes de comearmos, vou explicar rapidamente o diagrama acima, a proposta desacoplarmos da nossa aplicao dependncias externas, como por exemplo o React Navigation, para isso utilizaremos um Adapter. Outro ponto interessante a camada de Domnio, onde nossa camada de Presentation no conhece nada da camada de Data e Infra, a comunicao entre essas camadas se d atravs da camada de Domnio, ser ela que utilizaremos no nosso Presenter para realizar as navegaes.

Comeando pelo Domnio:

export interface Navigate {  navigateToMyPlans(params?: RouteParams): void;}

Em direo ao Data:

export class NavigateScreenMyPlans implements Navigate {  constructor(readonly navigateScreen: NavigateScreen) {}  navigateToMyPlans(params?: RouteParams | undefined): void {    this.navigateScreen.navigate(Routes.ACTIVITY, params);  }}
export interface NavigateScreen {  navigate(routeName: string, params?: GenericObject | undefined): void;}

E somente agora utilizaremos as aes do React Navigation na camada de Infra:

export class ReactNavigationAdapter implements NavigateScreen {  constructor(readonly navigation: NavigationContainerRef<any>) {}  navigate(routeName: string, params: GenericObject | undefined): void {    this.navigation.dispatch(      CommonActions.navigate({ name: routeName, params: params }),    );  }}

Chamando a Ao de Navigate no nosso Presenter

timo, agora que o Core da nossa aplicao est pronta. Agora vem a parte mais simples, que realizarmos a ao de navegar atravs da nossa camada de Dominio que ser passada via props.

type Props = {  navigate: Navigate;};const WelcomePresenter: React.FC<Props> = ({ navigate }) => {  const [toggleEnabled, componentsToggle] = useState(false);  const buttonAction = () => {    navigate.navigateToMyPlans();  };  return (    <Welcome      buttonAction={buttonAction}      componentsToggle={componentsToggle}      toggleEnabled={toggleEnabled}    />  );};export default WelcomePresenter;

Ento se pararmos para observar, em nenhum momento no nosso Presenter, sabemos que estamos utilizando o React Navigation para realizar a navegao. O que estamos utilizando apenas uma Interface que passada via props.

Mas em que momento passamos via props? Para isso vamos utilizar um Factory onde nele faremos a composio das dependncias necessrias.

type Props = {  route: RouteProp<StackParams, Routes>;  navigation: any;};const WelcomeFactory: React.FC<Props> = () => {  const navigate = useNavigate();  const navigateScreen = new NavigateScreenMyPlans(navigate);  return <Welcome navigate={navigateScreen} />;};export default WelcomeFactory;

Escrevendo alguns cenrios de Testes Unitrios

Por ultimo, mas no menos importante. Vamos ver como se torna mais fcil e claro, os testes unitrios envolvendo a navegao.

Como foi utilizado o TDD, todas as camadas envolvendo a navegao possuem testes unitrios, mas como o foco aqui o desacoplamento do React Navigation do nosso Presenter, ento vou mostrar somente o teste feito no nosso Presenter.

describe('Presentation: Welcome', () => {  test('should navigate with success when button press', () => {    const { sut, navigateToMyPlansSpy } = makeSut();    const button = sut.getByTestId('button_id');    fireEvent.press(button);    expect(navigateToMyPlansSpy).toHaveBeenCalledTimes(1);  });});const makeSut = () => {  let navigation = {} as NavigationContainerRef<any>;  render(    <Navigation      setNavigationTop={(navigationRef) => (navigation = navigationRef)}      initialRouteName={Routes.WELCOME}    />,  );  const navigate = new NavigateScreenMyPlans(navigation);  const navigateToMyPlansSpy = jest.spyOn(navigate, 'navigateToMyPlans');  const sut = render(<Welcome navigate={navigate} />);  return { sut, navigateToMyPlansSpy };};

Como foi feito todo o desacoplamento da navegao, no precisamos mais testar toda a aplicao at chegar no objetivo do teste. Basta criar a navegao e passar o navigate via props.

Bem, chegamos ao final do artigo, queria compartilhar essa ideia com vocs, onde mostro como podemos remover essa dependncia direta do React Navigation dos nossos Presenter's e assim termos maior liberdade e facilidade para um dia trocarmos de bibliotecas de navegao sem grandes problemas, alm de deixar o nosso Presenter menos acoplado.

Valeu galera, at a prxima.


Original Link: https://dev.to/marlonbelomarques/como-desacoplar-a-navegacao-no-react-native-5c72

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To