Kodax

تحسين الأداء — React Native المتقدم

تحسين أداء React Native

1. FlatList Optimization

// ❌ سيء
<FlatList
  data={items}
  renderItem={({ item }) => (
    <TouchableOpacity onPress={() => handlePress(item)}>
      <Text>{item.name}</Text>
    </TouchableOpacity>
  )}
/>

// ✅ جيد
const renderItem = useCallback(({ item }) => (
  <ListItem item={item} onPress={handlePress} />
), [handlePress]);

<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={(item) => item.id}
  initialNumToRender={10}
  maxToRenderPerBatch={10}
  windowSize={5}
  removeClippedSubviews={true}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>

// مكون العنصر مع memo
const ListItem = React.memo(({ item, onPress }) => (
  <TouchableOpacity onPress={() => onPress(item)}>
    <Text>{item.name}</Text>
  </TouchableOpacity>
));

2. React.memo للمكونات

// ❌ يُعاد رسمه في كل مرة
const SlowList = ({ data, onUpdate }) => {
  return (
    <View>
      {data.map(item => <Text key={item.id}>{item.name}</Text>)}
    </View>
  );
};

// ✅ يُعاد رسمه فقط عند تغير props
const MemoList = React.memo(({ data, onUpdate }) => {
  return (
    <View>
      {data.map(item => <Text key={item.id}>{item.name}</Text>)}
    </View>
  );
});

// مع مقارنة مخصصة
const MemoListByLength = React.memo(
  ({ data, onUpdate }) => {
    // ...
  },
  (prevProps, nextProps) => {
    return prevProps.data.length === nextProps.data.length;
  }
);

3. useMemo و useCallback

function SearchScreen() {
  const [query, setQuery] = useState('');
  const [items, setItems] = useState([]);

  // ❌ يُحسب في كل render
  const filteredSlow = items.filter(item =>
    item.name.toLowerCase().includes(query.toLowerCase())
  );

  // ✅ يُحسب فقط عند تغير items أو query
  const filteredItems = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase().includes(query.toLowerCase())
    );
  }, [items, query]);

  // ❌ دالة جديدة في كل render
  const handlePressSlow = (item) => {
    console.log(item);
  };

  // ✅ نفس الدالة طالما لم تتغير dependencies
  const handlePress = useCallback((item) => {
    console.log(item);
  }, []);

  return (
    <FlatList
      data={filteredItems}
      renderItem={({ item }) => (
        <ListItem item={item} onPress={handlePress} />
      )}
    />
  );
}

4. تحسين الصور

npm install expo-image
import { Image } from 'expo-image';

// بدلاً من Image العادي
<Image
  source={{ uri: imageUrl }}
  style={{ width: 200, height: 200 }}
  contentFit="cover"
  transition={200}
  cachePolicy="memory-disk"
  placeholder={blurhash}
/>

// Blurhash للتحميل
const blurhash = 'L6PZfSi_.AyE_3t7t7R**0o#DgR4';

5. تجنب re-renders غير ضرورية

function Row({ item, items, handlePress }) {
  // ✅ دالة ثابتة بدل دالة جديدة في كل رسم
  const handleItemPress = useCallback(() => handlePress(item), [item, handlePress]);

  return (
    <>
      {/* ❌ سيء: كائن أنماط جديد في كل رسم */}
      <View style={{ backgroundColor: 'red', padding: 10 }} />
      {/* ✅ جيد: StyleSheet */}
      <View style={styles.container} />

      {/* ❌ سيء: دالة سهمية جديدة في كل رسم */}
      <TouchableOpacity onPress={() => handlePress(item)} />
      {/* ✅ جيد */}
      <TouchableOpacity onPress={handleItemPress} />

      {/* ❌ سيء: بلا key ومكوّن مضمّن */}
      {items.map((i) => <View>{i.name}</View>)}
      {/* ✅ جيد: مكوّن منفصل مع key */}
      {items.map((i) => <ListItem key={i.id} item={i} />)}
    </>
  );
}

6. Hermes Engine (تحسين كبير)

في app.json:

{
  "expo": {
    "jsEngine": "hermes"
  }
}

7. قياس الأداء

// قياس وقت التنفيذ
console.time('fetchData');
await fetchData();
console.timeEnd('fetchData');

// React DevTools Profiler
import { Profiler } from 'react';

<Profiler
  id="FlatList"
  onRender={(id, phase, actualDuration) => {
    console.log(`${id} took ${actualDuration}ms`);
  }}
>
  <FlatList data={items} renderItem={renderItem} />
</Profiler>
اختبر فهمك