Sviluppo App React Native 2026: Guida Completa e Domande di Colloquio

La guida completa allo sviluppo di app React Native nel 2026. JSI, Fabric, New Architecture, ottimizzazione delle performance e domande frequenti nei colloqui tecnici.

Sviluppo App React Native 2026 - Guida Completa

Lo sviluppo di app React Native nel 2026 ha subito una trasformazione radicale. Con la versione 0.87, la New Architecture è diventata l'unica opzione supportata. Il vecchio bridge che serializzava ogni chiamata JavaScript-to-native in JSON non esiste più, sostituito da JSI (JavaScript Interface) per la comunicazione sincrona diretta.

Novità in React Native 0.87

React Native 0.87 rende obbligatoria la New Architecture. Il vecchio bridge è stato completamente rimosso. Il rendering di liste complesse è più veloce del 43%, i frame drop durante lo scroll sono diminuiti del 95% e l'utilizzo della memoria si è ridotto del 33% nei test di benchmark.

Comprendere JSI e la New Architecture

JSI (JavaScript Interface) sostituisce il bridge asincrono con binding C++ diretti tra JavaScript e codice nativo. Invece di serializzare i dati in JSON, passarli attraverso una coda di messaggi e deserializzarli dall'altra parte, JSI permette a JavaScript di mantenere riferimenti agli oggetti nativi e chiamare i loro metodi direttamente.

Questo cambiamento architetturale abilita tre capacità fondamentali: chiamate native sincrone, ownership condiviso degli oggetti tra i layer JavaScript e nativo, e lazy loading dei moduli nativi.

NativeUserModule.tstypescript
import { TurboModuleRegistry, TurboModule } from 'react-native';

export interface Spec extends TurboModule {
  getUserProfile(userId: string): Promise<{
    id: string;
    name: string;
    email: string;
  }>;
  
  // Chiamata sincrona - possibile solo con JSI
  getDeviceLocale(): string;
}

export default TurboModuleRegistry.getEnforcing<Spec>('UserModule');

Il metodo getDeviceLocale() restituisce immediatamente senza attendere una Promise. Questo era impossibile con il vecchio bridge, dove ogni chiamata nativa richiedeva serializzazione asincrona.

Costruire Componenti Production-Ready con Fabric

Fabric è il nuovo sistema di rendering che lavora insieme a JSI. Gestisce l'albero UI in C++ e si sincronizza direttamente con il thread JavaScript, eliminando il "UI jank" che si verificava quando il vecchio bridge non riusciva a tenere il passo durante aggiornamenti rapidi.

ProductCard.tsxtypescript
import React, { memo, useCallback } from 'react';
import {
  View,
  Text,
  Image,
  Pressable,
  StyleSheet,
} from 'react-native';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

interface Product {
  id: string;
  name: string;
  price: number;
  imageUrl: string;
  inStock: boolean;
}

interface ProductCardProps {
  product: Product;
  onPress: (id: string) => void;
}

const AnimatedPressable = Animated.createAnimatedComponent(Pressable);

export const ProductCard = memo(function ProductCard({
  product,
  onPress,
}: ProductCardProps) {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  const handlePressIn = useCallback(() => {
    scale.value = withSpring(0.95);
  }, [scale]);

  const handlePressOut = useCallback(() => {
    scale.value = withSpring(1);
  }, [scale]);

  const handlePress = useCallback(() => {
    onPress(product.id);
  }, [onPress, product.id]);

  return (
    <AnimatedPressable
      style={[styles.card, animatedStyle]}
      onPressIn={handlePressIn}
      onPressOut={handlePressOut}
      onPress={handlePress}
    >
      <Image
        source={{ uri: product.imageUrl }}
        style={styles.image}
        resizeMode="cover"
      />
      <View style={styles.content}>
        <Text style={styles.name} numberOfLines={2}>
          {product.name}
        </Text>
        <Text style={styles.price}>
{product.price.toFixed(2)}
        </Text>
        {!product.inStock && (
          <Text style={styles.outOfStock}>Non disponibile</Text>
        )}
      </View>
    </AnimatedPressable>
  );
});

const styles = StyleSheet.create({
  card: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    elevation: 3,
    overflow: 'hidden',
  },
  image: {
    width: '100%',
    height: 200,
  },
  content: {
    padding: 16,
  },
  name: {
    fontSize: 16,
    fontWeight: '600',
    color: '#1a1a1a',
    marginBottom: 8,
  },
  price: {
    fontSize: 18,
    fontWeight: '700',
    color: '#2563eb',
  },
  outOfStock: {
    fontSize: 14,
    color: '#dc2626',
    marginTop: 4,
  },
});

La combinazione di memo, useCallback e useSharedValue assicura che le animazioni vengano eseguite sul thread UI senza bloccare il thread JavaScript. react-native-reanimated si integra direttamente con Fabric e può eseguire animazioni a 60+ FPS.

Gestione dello State con Zustand

Zustand si è affermato come la soluzione preferita per la gestione dello state in React Native. Il boilerplate minimo e la compatibilità con Concurrent React lo rendono ideale per le applicazioni mobile.

stores/cartStore.tstypescript
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

interface CartItem {
  productId: string;
  quantity: number;
  price: number;
}

interface CartStore {
  items: CartItem[];
  addItem: (productId: string, price: number) => void;
  removeItem: (productId: string) => void;
  updateQuantity: (productId: string, quantity: number) => void;
  clearCart: () => void;
  total: () => number;
}

export const useCartStore = create<CartStore>()(
  persist(
    (set, get) => ({
      items: [],
      
      addItem: (productId, price) => set((state) => {
        const existing = state.items.find(i => i.productId === productId);
        if (existing) {
          return {
            items: state.items.map(i =>
              i.productId === productId
                ? { ...i, quantity: i.quantity + 1 }
                : i
            ),
          };
        }
        return {
          items: [...state.items, { productId, quantity: 1, price }],
        };
      }),
      
      removeItem: (productId) => set((state) => ({
        items: state.items.filter(i => i.productId !== productId),
      })),
      
      updateQuantity: (productId, quantity) => set((state) => ({
        items: quantity > 0
          ? state.items.map(i =>
              i.productId === productId ? { ...i, quantity } : i
            )
          : state.items.filter(i => i.productId !== productId),
      })),
      
      clearCart: () => set({ items: [] }),
      
      total: () => {
        return get().items.reduce(
          (sum, item) => sum + item.price * item.quantity,
          0
        );
      },
    }),
    {
      name: 'cart-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);

Lo store persiste il carrello in AsyncStorage e ripristina automaticamente lo stato al riavvio dell'app. La composizione dei middleware rende tutto questo possibile con una configurazione minima.

Expo Router porta il routing basato su file in React Native. La struttura delle cartelle definisce la navigazione dell'app, in modo simile a Next.js nell'ecosistema web.

app/_layout.tsxtypescript
import { Stack } from 'expo-router';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5,
      retry: 2,
    },
  },
});

export default function RootLayout() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <QueryClientProvider client={queryClient}>
        <Stack
          screenOptions={{
            headerShown: false,
            animation: 'slide_from_right',
          }}
        />
      </QueryClientProvider>
    </GestureHandlerRootView>
  );
}
app/(tabs)/_layout.tsxtypescript
import { Tabs } from 'expo-router';
import { Home, Search, ShoppingCart, User } from 'lucide-react-native';

export default function TabLayout() {
  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: '#2563eb',
        tabBarInactiveTintColor: '#6b7280',
        headerShown: false,
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: 'Home',
          tabBarIcon: ({ color, size }) => (
            <Home size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="search"
        options={{
          title: 'Cerca',
          tabBarIcon: ({ color, size }) => (
            <Search size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="cart"
        options={{
          title: 'Carrello',
          tabBarIcon: ({ color, size }) => (
            <ShoppingCart size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="profile"
        options={{
          title: 'Profilo',
          tabBarIcon: ({ color, size }) => (
            <User size={size} color={color} />
          ),
        }}
      />
    </Tabs>
  );
}

Data Fetching con TanStack Query

TanStack Query (precedentemente React Query) gestisce lo state del server. La libreria si occupa di caching, revalidazione e aggiornamenti in background, permettendo agli sviluppatori di concentrarsi sulla UI.

hooks/useProducts.tstypescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '../lib/api';

interface Product {
  id: string;
  name: string;
  price: number;
  imageUrl: string;
  inStock: boolean;
  category: string;
}

export function useProducts(category?: string) {
  return useQuery({
    queryKey: ['products', category],
    queryFn: () => api.get<Product[]>(`/products`, {
      params: category ? { category } : undefined,
    }),
    staleTime: 1000 * 60 * 5,
  });
}

export function useProduct(id: string) {
  return useQuery({
    queryKey: ['product', id],
    queryFn: () => api.get<Product>(`/products/${id}`),
    enabled: !!id,
  });
}

export function useUpdateProduct() {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: (data: Partial<Product> & { id: string }) =>
      api.patch<Product>(`/products/${data.id}`, data),
    onSuccess: (updatedProduct) => {
      queryClient.setQueryData(
        ['product', updatedProduct.id],
        updatedProduct
      );
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });
}

Ottimizzazione delle Performance per le Liste

FlashList di Shopify sostituisce FlatList per visualizzazioni di liste ad alte prestazioni. La libreria ricicla i componenti in modo aggressivo e sfrutta l'architettura Fabric per uno scrolling fluido.

components/ProductList.tsxtypescript
import React, { useCallback } from 'react';
import { View, StyleSheet, RefreshControl } from 'react-native';
import { FlashList } from '@shopify/flash-list';
import { useProducts } from '../hooks/useProducts';
import { ProductCard } from './ProductCard';
import { ProductSkeleton } from './ProductSkeleton';

interface ProductListProps {
  category?: string;
  onProductPress: (id: string) => void;
}

export function ProductList({ category, onProductPress }: ProductListProps) {
  const {
    data: products,
    isLoading,
    isRefetching,
    refetch,
  } = useProducts(category);

  const renderItem = useCallback(
    ({ item }: { item: Product }) => (
      <ProductCard product={item} onPress={onProductPress} />
    ),
    [onProductPress]
  );

  const keyExtractor = useCallback(
    (item: Product) => item.id,
    []
  );

  if (isLoading) {
    return (
      <View style={styles.skeletonContainer}>
        {Array.from({ length: 6 }).map((_, i) => (
          <ProductSkeleton key={i} />
        ))}
      </View>
    );
  }

  return (
    <FlashList
      data={products}
      renderItem={renderItem}
      keyExtractor={keyExtractor}
      estimatedItemSize={280}
      numColumns={2}
      contentContainerStyle={styles.listContent}
      refreshControl={
        <RefreshControl
          refreshing={isRefetching}
          onRefresh={refetch}
          tintColor="#2563eb"
        />
      }
      ItemSeparatorComponent={() => <View style={styles.separator} />}
    />
  );
}

const styles = StyleSheet.create({
  listContent: {
    padding: 16,
  },
  separator: {
    height: 16,
  },
  skeletonContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    padding: 16,
    gap: 16,
  },
});

La proprietà estimatedItemSize è cruciale per le performance. FlashList utilizza questo valore per il layout iniziale e poi si adatta in base alle misurazioni effettive.

Domande Frequenti nei Colloqui per Sviluppatori React Native

Durante i colloqui per posizioni React Native, alcuni argomenti vengono regolarmente affrontati. Le seguenti domande e risposte coprono le aree più importanti.

Qual è la differenza tra il vecchio bridge e JSI?

Il vecchio bridge utilizzava la serializzazione JSON asincrona per la comunicazione tra JavaScript e Native. JSI abilita binding C++ diretti, chiamate sincrone e ownership condiviso degli oggetti. Questo elimina l'overhead della serializzazione e permette veri moduli nativi.

Come funziona Fabric rispetto al vecchio renderer?

Fabric renderizza gli alberi UI in C++ invece che in Java/Objective-C. Questo permette misurazioni sincrone, aggiornamenti prioritizzati e integrazione diretta con JSI. Lo Shadow Tree ora vive nel layer C++, riducendo drasticamente la comunicazione tra thread.

Quando si dovrebbero usare useCallback e useMemo?

useCallback dovrebbe essere usato per gli event handler passati ai componenti figli. useMemo è utile per calcoli costosi che dipendono da props o state. Entrambi dovrebbero essere usati con parsimonia poiché hanno un proprio overhead.

Come si ottimizzano le performance di una FlatList?

Usare FlashList come alternativa a FlatList, fornire getItemLayout o estimatedItemSize, abilitare removeClippedSubviews, wrappare gli item della lista con memo e definire keyExtractor al di fuori della funzione di render.

Pronto a superare i tuoi colloqui su React Native?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Conclusione

Lo sviluppo di app React Native nel 2026 richiede una comprensione della New Architecture con JSI e Fabric. La combinazione di Expo Router per la navigazione, TanStack Query per lo state del server e Zustand per lo state client forma una solida base per applicazioni mobile pronte per la produzione. FlashList e react-native-reanimated garantiscono le performance che gli utenti si aspettano dalle app native. Gli sviluppatori che padroneggiano queste tecnologie sono perfettamente preparati per i progetti React Native attuali e per i colloqui tecnici.

Sfida del giorno

Sapresti trovare il bug in React Native?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 6 settembre 2026

Tag

#react-native
#mobile-development
#javascript
#typescript
#jsi
#fabric

Condividi

Articoli correlati