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.
- 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>
);
};