← Regresar a lecciones

Estado, ciclo de vida y renderizado dinámico en React Native con TypeScript

Estado local con useState
  • Ejemplo básico con TypeScript

React Native nos permite crear aplicaciones móviles nativas con la simplicidad declarativa de React. Cuando agregamos TypeScript, ganamos claridad y seguridad: sabemos qué tipo de datos maneja cada componente, y el editor nos ayuda a evitar errores en tiempo real.

En este artículo aprenderás tres pilares esenciales del desarrollo moderno con React Native:

  1. useState — manejo de estado local
  2. useEffect — control del ciclo de vida
  3. Renderizado condicional y listas

Estado local con useState

Antes de entender useState, necesitamos imaginar cómo piensa React. Cada componente es como una pequeña máquina de estados; cuando esos datos cambian, la interfaz debe cambiar automáticamente.

En una app móvil real, el estado puede representar muchas cosas:

  • El número de veces que un usuario presiona un botón.
  • El texto que está escribiendo en un campo de formulario.
  • Si un usuario está o no logueado.
  • Un interruptor encendido o apagado.

En React, estos valores cambiantes se guardan con el hook useState. Este hook nos permite recordar datos entre renderizados y actualizarlos de forma reactiva, sin manipular la UI manualmente.

Ejemplo básico con TypeScript

1import React, { useState } from 'react'; 2import { View, Text, Button } from 'react-native'; 3 4export default function Counter(): JSX.Element { 5 const [count, setCount] = useState<number>(0); 6 7 return ( 8 <View style={{ padding: 20 }}> 9 <Text style={{ fontSize: 24 }}>Has hecho clic {count} veces</Text> 10 <Button title="Incrementar" onPress={() => setCount(count + 1)} /> 11 </View> 12 ); 13}

Explicación

  • useState<number>(0) crea una variable de estado count que solo puede ser un número.
  • setCount actualiza el estado y provoca un re-render automático.
  • Cada clic muestra el nuevo valor en pantalla.

💡 En Kotlin, esto sería similar a usar un MutableStateFlow<Int> o un LiveData<Int> observado por la UI.

Ciclo de vida con useEffect

useEffect se usa para ejecutar código después de que el componente se renderiza, o cuando cambian ciertos valores. Es el equivalente a onCreate y onDestroy en Android, o viewDidLoad y viewDidDisappear en iOS, pero expresado de forma declarativa.

Ejemplo 1: montaje y desmontaje

1import React, { useEffect } from 'react'; 2import { View, Text } from 'react-native'; 3 4export default function SimpleEffect(): JSX.Element { 5 useEffect(() => { 6 console.log('🟢 Componente montado'); 7 8 return () => { 9 console.log('🔴 Componente desmontado'); 10 }; 11 }, []); 12 13 return ( 14 <View style={{ padding: 20 }}> 15 <Text style={{ fontSize: 20 }}>Observa la consola</Text> 16 </View> 17 ); 18}

Qué ocurre

  • Al aparecer el componente, se ejecuta el primer bloque.
  • Al desaparecer, se ejecuta la función de limpieza (return).

Equivalencias conceptuales:

AndroidReact NativeSwift
onCreate()useEffect(() => {...}, [])viewDidLoad()
onDestroy()return () => {...}viewDidDisappear()

Ejemplo 2: temporizador con limpieza

1import React, { useState, useEffect } from 'react'; 2import { View, Text } from 'react-native'; 3 4export default function Timer(): JSX.Element { 5 const [seconds, setSeconds] = useState<number>(0); 6 7 useEffect(() => { 8 console.log('⏱ Temporizador iniciado'); 9 10 const interval = setInterval(() => { 11 setSeconds((prev) => prev + 1); 12 }, 1000); 13 14 return () => { 15 console.log('🧹 Temporizador detenido'); 16 clearInterval(interval); 17 }; 18 }, []); 19 20 return ( 21 <View style={{ padding: 20 }}> 22 <Text style={{ fontSize: 22 }}>Segundos: {seconds}</Text> 23 </View> 24 ); 25}

En Kotlin sería:

1override fun onCreate(savedInstanceState: Bundle?) { 2 timer = Timer() 3 timer.scheduleAtFixedRate(object : TimerTask() { 4 override fun run() { 5 seconds++ 6 } 7 }, 0, 1000) 8} 9 10override fun onDestroy() { 11 timer.cancel() 12}

Y en Swift:

1override func viewDidLoad() { 2 timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in 3 seconds += 1 4 } 5} 6 7override func viewDidDisappear(_ animated: Bool) { 8 timer.invalidate() 9}

🪄 En React Native lo haces con una sola estructura, más declarativa y predecible.

Ejemplo 3: efecto dependiente de un valor

1import React, { useState, useEffect } from 'react'; 2import { View, Text, Button } from 'react-native'; 3 4export default function DependentEffect(): JSX.Element { 5 const [count, setCount] = useState<number>(0); 6 7 useEffect(() => { 8 console.log(`El contador cambió a: ${count}`); 9 }, [count]); // solo se ejecuta cuando cambia count 10 11 return ( 12 <View style={{ padding: 20 }}> 13 <Text style={{ fontSize: 20 }}>Contador: {count}</Text> 14 <Button title="Incrementar" onPress={() => setCount(count + 1)} /> 15 </View> 16 ); 17}

💡 En Kotlin podrías compararlo con observar un LiveData<Int> y en Swift con una propiedad didSet.

3. Renderizado condicional y listas

El renderizado condicional permite mostrar diferentes vistas según el estado.

1import React, { useState } from 'react'; 2import { View, Text, Button } from 'react-native'; 3 4export default function LoginExample(): JSX.Element { 5 const [loggedIn, setLoggedIn] = useState<boolean>(false); 6 7 return ( 8 <View style={{ padding: 20 }}> 9 {loggedIn ? ( 10 <> 11 <Text style={{ fontSize: 20 }}>¡Bienvenido de nuevo!</Text> 12 <Button title="Cerrar sesión" onPress={() => setLoggedIn(false)} /> 13 </> 14 ) : ( 15 <> 16 <Text style={{ fontSize: 20 }}>Por favor, inicia sesión</Text> 17 <Button title="Iniciar sesión" onPress={() => setLoggedIn(true)} /> 18 </> 19 )} 20 </View> 21 ); 22}

Esto es similar a usar un if o un when en Kotlin, pero dentro del propio JSX, mezclando lógica y presentación.

Listas dinámicas

Para renderizar listas en React Native se usa FlatList, optimizada para rendimiento.

1import React from 'react'; 2import { View, Text, FlatList } from 'react-native'; 3 4interface Task { 5 id: string; 6 text: string; 7} 8 9export default function TodoList(): JSX.Element { 10 const tasks: Task[] = [ 11 { id: '1', text: 'Aprender useState' }, 12 { id: '2', text: 'Practicar useEffect' }, 13 { id: '3', text: 'Dominar el renderizado condicional' }, 14 ]; 15 16 return ( 17 <View style={{ padding: 20 }}> 18 <FlatList 19 data={tasks} 20 keyExtractor={(item) => item.id} 21 renderItem={({ item }) => ( 22 <Text style={{ fontSize: 18, marginVertical: 4 }}>{item.text}</Text> 23 )} 24 /> 25 </View> 26 ); 27}

En resumen

ConceptoKotlinSwift / SwiftUIReact Native
EstadoLiveData, MutableStateFlow@State, @PublisheduseState
Efectos de ciclo de vidaonCreate(), onDestroy().onAppear {}, .onDisappear {}useEffect
Vista condicionalif, whenif, switch{condición ? <A/> : <B/>}
Listas dinámicasRecyclerViewListFlatList

React Native con TypeScript te enseña a pensar en estados y efectos, no en instrucciones. El flujo de datos es unidireccional, y los componentes reaccionan automáticamente a los cambios, sin que tú tengas que “decirles qué hacer”.