Cómo usar Zustand en tu proyecto React Native con ejemplos prácticos

En este artículo aprenderás a estructurar tu proyecto con React Navigation y posteriormente implementaremos Zustand para manejar el estado global. Incluimos ejemplos prácticos como un contador dinámico y un estado de perfil, junto con ejercicios para que pongas en práctica lo aprendido.


  1. Recuerda que para inicializar un proyecto al dia de la fecha es con npx @react-native-community/cli init MiProyecto

1. Bonus: Crear un Bottom Navigation

Antes de comenzar con Zustand, crearemos un Bottom Navigation para nuestra aplicación. Este patrón de navegación es común en aplicaciones móviles y nos permite organizar las pantallas de forma intuitiva.

1.1. Pre-requisitos

Antes de implementar el Bottom Navigation, asegúrate de configurar correctamente React Navigation siguiendo esta guía oficial:

Asegúrate de instalar las dependencias necesarias:

npm install @react-navigation/native react-native-screens react-native-safe-area-context react-native-gesture-handler react-native-reanimated react-native-vector-icons
npm install @react-navigation/bottom-tabs

No olvides configurar react-native-gesture-handler en el archivo de entrada (index.js o App.js).


1.2. Archivo: presentation/navigators/BottomNav.tsx

El siguiente código define un Bottom Navigation con tres pantallas: Home, Profile y Settings.

import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { ProfileScreen } from '../screens/profile/ProfileScreen';
import { HomeScreen } from '../screens/home/HomeScreen';
import { SettingsScreen } from '../screens/settings/Settings';

const Tab = createBottomTabNavigator();

export const BottomNav = () => {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
};

1.3. Organización del proyecto

Tu proyecto debería tener una estructura similar a esta:

src/
├── config/
│   └── theme.tsx
├── presentation/
│   ├── navigators/
│   │   └── BottomNav.tsx
│   └── screens/
│       ├── home/
│       │   └── HomeScreen.tsx
│       ├── profile/
│       │   └── ProfileScreen.tsx
│       └── settings/
│           └── Settings.tsx
├── store/
└── Main.tsx

Esto asegura que tengas una separación clara entre las pantallas y los navegadores.


2. Instalación de Zustand

Ahora que tenemos nuestro Bottom Navigation, es momento de manejar estados globales con Zustand. Primero, instala la biblioteca:

npm install zustand

3. Creación del estado global con Zustand

3.1. Archivo: store/profile-store.ts

Este archivo maneja el estado global del perfil del usuario:

import { create } from 'zustand';

export interface ProfileState {
  name: string;
  email: string;
  changeProfile: (name: string, email: string) => void;
}

export const useProfileStore = create<ProfileState>()((set) => ({
  name: 'Nahuel',
  email: 'nahuel.dev.23@gmail.com',
  changeProfile: (name: string, email: string) => {
    set({ name, email });
  },
}));

3.2. Pantalla: ProfileScreen

En esta pantalla puedes interactuar con el estado global:

import React from 'react';
import { Pressable, Text, View } from 'react-native';
import { useProfileStore } from '../../store/profile-store';

export const ProfileScreen = () => {
  const name = useProfileStore((state) => state.name);
  const email = useProfileStore((state) => state.email);
  const changeProfile = useProfileStore((state) => state.changeProfile);

  return (
    <View>
      <Text>{name}</Text>
      <Text>{email}</Text>

      <Pressable onPress={() => useProfileStore.setState({ name: 'Mileno' })}>
        <Text>Cambiar nombre</Text>
      </Pressable>

      <Pressable onPress={() => useProfileStore.setState({ email: 'Mileno@gmail.com' })}>
        <Text>Cambiar email</Text>
      </Pressable>

      <Pressable onPress={() => changeProfile('Nahuel', 'nahuel.dev.23@gmail.com')}>
        <Text>Reestablecer valores</Text>
      </Pressable>
    </View>
  );
};

4. Ejercicio práctico: Crear un contador

Ejercicio

Crea un contador que permita incrementar y decrementar su valor. El valor del contador debe mostrarse tanto en la pantalla SettingsScreen como en el título de la misma.

Solución

4.1. Archivo: store/counter-store.ts

import { create } from 'zustand';

export interface CounterState {
  counter: number;
  changeCounter: (value: number) => void;
}

export const useCounterState = create<CounterState>()((set, get) => ({
  counter: 0,
  changeCounter: (value: number) => {
    set({ counter: get().counter + value });
  },
}));

4.2. Pantalla: SettingsScreen

import { useNavigation } from '@react-navigation/native';
import React, { useEffect } from 'react';
import { Pressable, Text, View } from 'react-native';
import { useCounterState } from '../../store/counter-store';

export const SettingsScreen = () => {
  const navigation = useNavigation();
  const counter = useCounterState((state) => state.counter);
  const changeCounter = useCounterState((state) => state.changeCounter);

  useEffect(() => {
    navigation.setOptions({
      title: `Counter: ${counter}`,
    });
  }, [counter, navigation]);

  return (
    <View>
      <Text>Count: {counter}</Text>
      <Pressable onPress={() => changeCounter(1)}>
        <Text>Incrementar</Text>
      </Pressable>
      <Pressable onPress={() => changeCounter(-1)}>
        <Text>Decrementar</Text>
      </Pressable>
    </View>
  );
};