Compartiendo Información entre Pantallas en React Native

Compartiendo Información entre Pantallas en React Native

Con React Native, a menudo nos encontramos con diferentes y pequeños desafíos y un caso muy común es el de compartir información entre pantallas. Si bien esto puede parecer trivial, hay diversas técnicas disponibles, y elegir la adecuada puede tener un impacto significativo en la eficiencia, claridad y mantenimiento de tu aplicación.

El Problema

Inicialmente había escrito mi aplicación encapsulando componentes y poniéndolos dentro de modales, de tal forma que toda la lógica se mantenía en una sola pantalla y solamente se abrían o cerraban modales según era necesario, pero después de algunas pruebas, noté que no en todos los dispositivos esta solución se comportaba aceptablemente bien y de ahí es que salió ¿Cómo compartes esos datos de manera eficiente?

Técnicas Comunes

  1. Modals:

Típicamente tienes una variable de estado que ante un evento cambias su valor para mostrar u ocultar el modal, la solución inicial fue algo como esto y fúe bastante rápido y simple de implementar.

   const [modalVisible, setModalVisible] = useState(false);
   const [selectedData, setSelectedData] = useState(null);
   <Modal 
     visible={modalVisible} 
     onRequestClose={() => setModalVisible(false)}
   >
     <YourComponent onSelectData={(data) => setSelectedData(data)} />
   </Modal>

Al cerrar el modal, selectedData contendrá la información seleccionada.

Pros:

  • Rápidos de implementar.
  • Buen control sobre la UX.

Contras:

  • Limitados en términos de animación y transición.
  • Puede ser más pesado en términos de recursos si se usa incorrectamente.
  1. React Navigation con Callbacks:

Pero despues de notar algunos problemas de rendimiento en equipos de gama baja, decidí empezar a explorar otras opciones y llegué a esta otra opción, donde la ScreenA para como parámetro a ScreenB un evento, de tal manera que ScreenB tiene la capacidad de ejecutar ese evento y así modificar lo que se necesite en ScreenA, más o menos el ejemplo se ve de la siguiente forma:

En ScreenA (la pantalla que abrirá ScreenB):

   const handleDataReturn = (data) => {
     // Hacer algo con los datos devueltos
   }

   navigation.navigate('ScreenB', { onReturn: handleDataReturn });

En ScreenB:

   const { onReturn } = route.params;

   // Al completar alguna acción
   if (onReturn) onReturn(someData);

Importante: el uso de navigation.onBack() aunque funciona en todos los casos, específicamente para retornar datos a la pantalla anterior no funciona.

Esta opción funcionó sin problemas y el cambio fue bastante sencillo partiendo del uso de modales, pues el código ya se encontraba encapsulado.

Pero en los logs noté un error que decía:

Protected > ScreenB > params.onReturn (Function) This can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/troubleshooting#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state for more details.

Pues era bastante claro que aunque funcionaba, no era la forma correcta.

Pros:

  • Muy flexible.
  • Integración completa con React Navigation.

Contras:

  • Puede complicarse con múltiples niveles de navegación.
  1. useContext:

Continué explorando y recordé que también podría utilizar useContext de React, pero abandoné esta idea porque introducía cierto nivel de complejidad para lo que realmente requería la tarea.

Más o menos el código podría verse de esta forma:

export const DataSharingContext = createContext();

export const DataSharingProvider = ({ children }) => {
  const [sharedData, setSharedData] = useState(null);

    return (
      <DataSharingContext.Provider value={{ sharedData, setSharedData }}>
        {children}
      </DataSharingContext.Provider>
    );
}

Y una vez creado el context, su uso del contexto en las pantallas va así:

   const { sharedData, setSharedData } = useContext(DataSharingContext);

Aparentemente es sencillo, pero definitivamente hay extra código que no esperaba manejar.

Pros:

  • Centraliza la gestión de datos.
  • Evita la "prop drilling".

Contras:

  • Si se abusa, puede complicar la gestión de estados.
  1. Usado State Managers (como Zustand, Redux, MobX, etc):

Este es igualmente sencillo de utilizar, pues solo requiere que se cree un estado global en cual se compartirá a través de toda la aplicación para después simplemente hacer triggers de actions.

En ejemplo rápido fue usando zustand:

   import create from 'zustand';

   const useStore = create(set => ({
     data: null,
     setData: (newData) => set(state => ({ data: newData }))
   }));

Uso en componentes:

   const { data, setData } = useStore();
   setDate({...}) // aqui lo que queremos modificar

Pros:

  • Gestión de estado global.
  • Puede mejorar la eficiencia en aplicaciones grandes.

Contras:

  • Añade complejidad y dependencias adicionales.

La Solución Ideal

Después de revisar a detalle cada una de las opciones mencionadas, todas parecían tener pros y contras, así es que decidí leer la documentación de React Navigation y encontré que simplemente puedes utilizar navigation.navigate('ScreenName', { data: yourData }). Si esa pantalla ya existe en el stack, actuará como goBack() pero pasando los datos necesarios, así de simple.

https://reactnavigation.org/docs/7.x/params/

Params aren't only useful for passing some data to a new screen, but they can also be useful to pass data to a previous screen too. For example, let's say you have a screen with a create post button, and the create post button opens a new screen to create a post. After creating the post, you want to pass the data for the post back to previous screen.

To achieve this, you can use the navigate method, which acts like goBack if the screen already exists. You can pass the params with navigate to pass the data back

Consideraciones de Memoria y Rendimiento

Realmente todas las soluciones son adecuadas en diferentes escenarios. Mientras que los modales y los manejadores de estado pueden ser ideales para aplicaciones pequeñas, las soluciones basadas en contextos o manejadores de estado globales pueden ser más adecuadas para aplicaciones más grandes, obviamente teniendo el contexto de que tipo de datos se compartirán y la complejidad de los componentes es que se puede decidir claramente que implementar.

Tabla Comparativa

Técnica Complejidad Extensibilidad Mantenimiento Consumo de Memoria
Modals Baja Baja Bajo Medio
Callback con React Navigation Media Alta Medio Bajo
useContext Media Alta Alto Medio
State Manager Alta Alta Alto Medio/Alto
React Navigation navigate() Baja Media Bajo Bajo

Conclusión

La solución usando navigation.navigate() tiene la ventaja de ser más directa y simple, lo que se refleja en su baja complejidad y consumo de memoria. Es una excelente opción cuando se busca una solución rápida y eficiente, especialmente cuando no se necesitan estructuras complejas o capacidades de extensibilidad avanzadas.

Cada proyecto es único, y lo que funciona para uno puede no ser ideal para otro, recuerda experimentar, evaluar, y sobre todo, mantén tu código limpio y legible para ti y tu equipo.

Happy coding! :D


Photo by Federico Burgalassi on Unsplash

Jack Fiallos

Jack Fiallos

Te gustó este artículo?