React Native ile Mobil Uygulama Geliştirme – 17 – Asyncstorage ile React Native’de Redux Persist Nasıl Kullanılır?

“React Native ile Mobil Uygulama Geliştirme” başlıklı makalelerimizden bir yenisini daha sizlerle paylaşmak istiyorum. Bu makale, bir dizi makaleden oluşacak olan serisinin 17. kısmıdır. Bir önceki makalede (Part 16),React Native ile geliştirilen mobil uygulamanızda, fetch API ile kullanıcılarınıza resim yükleme işlemini nasıl yapabileceğiniz anlattım.
Bu makalede Asyncstorage ile React Native’de Redux Persist ile uygulamalarda sayfa değiştiğinde, yenilendiğinde veya uygulama kapatıp tekrar açıldığında, tekrar eski haline dönmesini veya sıfırlanmasını engellemeyi öğreneceğiz.
Daha önceki makaleleri okumadıysanız, lütfen buradan başlayın.

Redux kullandığımız uygulamalarda, sayfa değiştiğinde, yenilendiğinde veya uygulama kapatıp tekrar açıldığında, redux’ taki değişkenlerin tekrar eski haline dönmesi veya sıfırlanması sebebiyle tekrar kullanmak istediğimizde ulaşamayız. Bu yüzden sayfa yenilendiğinde tekrar istek atar ve değişkenleri tekrar setleriz. Özellikle genelde servisten gelen sabit objeler veya arraylerde bu can sıkıcı hale gelir çünkü tekrar servise istek atıp değişkeni tekrar setlemek gerekir.

Redux persist, redux’taki değişkenlerin, sayfa yenilendiğinde, değiştiğinde veya uygulama kapatıp tekrar açıldığında tekrar eski haline dönmesini veya sıfırlanmasını engellemeye yarayan, bu değişkenleri localstorage da saklayan bir pakettir.

Redux Persist, bir uygulamanın yerel deposunda bir Redux store kaydedilmesine izin veren bir kitaplıktır. React Native terimleriyle, Asyncstorage, global olan ve uygulama için yerel depolama olarak kullanılabilen, anahtar/değer tabanlı, şifrelenmemiş, eşzamansız bir depolama sistemidir.

Bir React Native uygulamasında Redux gibi bir durum yönetimi kitaplığı kullanmak, bir uygulamanın durumunu tek bir yerden yönetmek için faydalıdır. Uygulamanız özellikler açısından ilerledikçe, her kullanıcı için yerel olan bazı bilgileri kalıcı kılmak isteyebilirsiniz.

Örneğin, bir alışveriş sepeti uygulaması oluşturuyorsunuz ve bir satın alma siparişi vermeden önce kullanıcının sepete eklediği ürünlerle ilgili verilerin kalıcı olmasını gerektiriyor. Kullanıcı, bu satın alma işlemini yapmadan önce uygulamayı keyfi bir nedenle kapatırsa ancak daha sonra geri dönerse ve bu sayıda öğenin sepetinden tamamen silineceğini görürse ne olur? Bu iyi bir kullanıcı deneyimi değil.

Bu kullanıcı deneyimini geliştirmek için öğeleri, cihazlarının yerel deposuna kaydedebilirsiniz. Asyncstorage ile birlikte redux-persist’in bir React Native uygulaması için kullanışlı olduğu yer burasıdır. Bu makalede, durum yönetimi kitaplığı olarak Redux kullanan bir React Native uygulamasında redux-persist kitaplığını kuracağız ve uygulamanın kapalı olduğu senaryolar için verileri Asyncstorage’da koruyacağız.

Gereksinimler
Bu makaleyi takip etmek için lütfen JavaScript/ES6’ya aşina olduğunuzdan ve yerel geliştirme ortamınızda aşağıdaki gereksinimleri karşıladığınızdan emin olun:

Node.js sürümü >= 12.x.x kurulu.
npm veya thread veya npx gibi bir paket yöneticisine erişim sağlayın.
Redux store, actions, and reducers, expo-cli, ve use npx hakkında temel bir anlayışa sahip olun.

expo-cli ile bir React Native uygulaması oluşturun
expo-cli kullanarak yeni bir React Native projesi oluşturun ve ardından bu demo uygulamasını oluşturmak için gereken bağımlılıkları yükleyin. Bir terminal penceresi açın ve aşağıdaki komutları yürütün:

npx expo init redux-persist-asyncstorage-example # navigate into that directory cd redux-persist-asyncstorage-example yarn add @react-navigation/native @react-navigation/bottom-tabs axios@0.21.0 redux@4.0.5 redux-persist@6.0.0 redux-thunk@2.3.0 react-redux@7.2.2 # install dependencies with Expo specific package version expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view @react-native-async-storage/async-storage

Bu bağımlılıkları yükledikten sonra demo uygulaması için çekirdek ekranlar olacak iki adet sahte ekran oluşturalım. Yeni bir screens/dizin oluşturun ve bunun içinde aşağıdaki kod parçacığıyla BooksList.js ilk ekran dosyasını oluşturun:

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function BooksListApp() {
  return (
    
      BooksList
    
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
});

Ardından, aşağıdaki kod parçacığıyla BookmarksList.js ikinci ekran dosyasını oluşturun:

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function BookmarksList() {
  return (
    
      BookmarksList
    
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
});

BooksList ekranı kitapların bir listesini gösterecek. Kitapları görüntülemek için verileri getireceğim ve temel URL olarak Draftbit‘in Örnek API yolunu kullanacağım.

Bu ekranda gösterilen her kitap öğesi, son kullanıcının daha sonra görüntülemek üzere gerçek zamanlı olarak işaretlemesi veya kaydetmesi için bir işlevselliğe sahip olacaktır. Kullanıcı tarafından kaydedilen tüm kitap öğeleri BookmarksList sekmesinde gösterilecektir.

Verileri getirmek için bir Temel URL gerektiğinden, onu ekleyelim. config/ adlı yeni bir dizin oluşturun ve bunun içinde index.js adlı bir dosya oluşturun ve aşağıdaki Temel URL’yi dışa aktarın:

export const BASE_URL = 'https://example-data.draftbit.com/books?_limit=10';

Artık bu Temel URL, HTTP istekleri göndermek için kullanıma hazırdır.

Ekranlar arasında geçiş yapmak için stab navigation ekleyin
Bu bölümde, uygulamanın bir önceki bölümde oluşturulan iki sahte ekranı göstermesi için altta özel bir sekme gezgini oluşturalım. Bir navigation/ dizin oluşturarak ve RootNavigator.js adlı yeni bir dosyanın içinde başlayın. Bu dosyaya aşağıdaki içe aktarma ifadelerini ekleyin:

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '@expo/vector-icons';

// Import mock screens
import BooksList from '../screens/BooksList';
import BookmarksList from '../screens/BookmarksList';

const Tab = createBottomTabNavigator();

Sekme çubuğu görünümünü özelleştirmek için, expo paketiyle önceden yüklenmiş olarak gelen @expo/vector-icons kitaplığından bazı stil ve özel simgeler ekleyelim.

const tabBarOptions = {
  showLabel: false,
  inactiveTintColor: '#2D3038',
  activeTintColor: '#FFFFFF',
  style: {
    height: '10%',
    backgroundColor: '#1E1B26'
  }
};

const screenOptions = (route, color) => {
  let iconName;

  switch (route.name) {
    case 'BooksList':
      iconName = 'view-dashboard';
      break;
    case 'BookmarksList':
      iconName = 'bookmark-multiple-outline';
      break;
    default:
      break;
  }

  return <MaterialCommunityIcons name={iconName} color={color} size={24} />;
};

tabBarOptions yapılandırma nesnesi, farklı uygulama ekranları arasında paylaşılan alt sekmenin görünümünü özelleştirecek. ScreenOptions, her sekme için özel bir simge eklemek için kullanılır.

Son olarak bu iki sekme ekranını oluşturacak RootNavigator bileşenini tanımlayıp dışa aktaralım.

const RootNavigator = () => {
  return (
    <NavigationContainer>
      <Tab.Navigator
        initialRouteName='BooksList'
        tabBarOptions={tabBarOptions}
        screenOptions={({ route }) => ({
          tabBarIcon: ({ color }) => screenOptions(route, color)
        })}
      >
        <Tab.Screen name='BooksList' component={BooksList} />
        <Tab.Screen name='BookmarksList' component={BookmarksList} />
      </Tab.Navigator>
    </NavigationContainer>
  );
};

export default RootNavigator;

RootNavigator’ı çalışırken görmek için, onu App.js dosyasının içine alın ve geri gönderin. App.js dosyasına aşağıdaki kod parçacığını ekleyin:

import React from 'react';

import RootNavigator from './navigation/RootNavigator';

export default function App() {
  return <RootNavigator />;
}

Uygulamayı çalıştırmak için terminal penceresinden yarn start komutunu çalıştırın.

İşte bu adımdan sonraki çıktı

Action Types ve creators ekleyin
Tüm uygulamanın durumunu yönetmek için Redux kullanıldığında, durumun kendisi bir JavaScript nesnesi tarafından temsil edilir. Bu nesne salt okunurdur, yani durum manipülasyonu doğrudan yapılmaz. Değişiklikler, eylemleri tetikleyerek yapılır.

Action Types tanımlayarak başlayalım. redux/ adında yeni bir dizin oluşturun ve bunun içinde action.js adında yeni bir dosya oluşturun. Aşağıdaki action types buna ekleyin:

// Define action types
export const GET_BOOKS = 'GET_BOOKS';
export const ADD_TO_BOOKMARK_LIST = 'ADD_TO_BOOKMARK_LIST';
export const REMOVE_FROM_BOOKMARK_LIST = 'REMOVE_FROM_BOOKMARK_LIST';

Yukarıdaki dosyada tanımlanan eylem türleri açıklayıcıdır. İlki, GET_BOOKS, Temel URL’den veri almak için HTTP isteğinde bulunmak için kullanılacaktır. İkincisi, ADD_TO_BOOKMARK_LIST, her kitap öğesini yer imleri listesine ekleyecektir. Benzer şekilde, üçüncü eylem türü REMOVE_FROM_BOOKMARK_LIST, kitabı yer imleri listesinden çıkaracaktır.

Redux kullanılarak depolanan durumu güncellemek için olayı tetiklemek için bir eylem türü kullanılır. Her eylem türünün bu amaç için eylem oluşturucuları vardır. Demo uygulamasında gereken ilk eylem oluşturucu, verileri Draftbit’in Örnek API’sinden getirmektir.

Verileri almak için axios adlı bir kütüphane kullanacağız. Uygun HTTP isteklerini yapmak için .get, .put vb. yöntemlerden oluşan bir API’ye sahiptir.

Verileri almak üzere HTTP isteğinde bulunmak için API’nin bir BASE URL’si gereklidir. Action.js dosyasının içinde, axios kitaplığını ve Temel URL’yi içe aktarın:

import axios from 'axios';

import { BASE_URL } from '../config';

 

Eylem türlerini tanımladıktan sonra, aşağıdaki kod parçacığıyla GET_BOOKS eylem türüne sahip getBooks adlı yeni bir eylem oluşturucu tanımlayın:

export const getBooks = () => {
  try {
    return async dispatch => {
      const response = await axios.get(`${BASE_URL}`);
      if (response.data) {
        dispatch({
          type: GET_BOOKS,
          payload: response.data
        });
      } else {
        console.log('Unable to fetch data from the API BASE URL!');
      }
    };
  } catch (error) {
    // Add custom logic to handle errors
    console.log(error);
  }
};

Reducer ekleme
Bir eylem tetiklendiğinde, uygulamanın durumu değişir. Uygulamanın durumunun işlenmesi bir redüktör tarafından yapılır.

Reducer, bir sonraki durumu ilk veya önceki duruma göre hesaplayan saf bir işlevdir. Durum değişmezse her zaman aynı çıktıyı üretir. State ve action olmak üzere iki girdi alır ve varsayılan state’i döndürmesi gerekir.

redux/ dizininde reducers.js adlı yeni bir dosya oluşturun. GET_BOOKS action türünü içe aktarın ve ardından ilk durumu iki boş diziyle tanımlayın. Ardından, ilk bağımsız değişken için varsayılan değer olarak initialState’i ve ikinci bağımsız değişken olarak eylemi alan bir bookReducer işlevi tanımlayın.

import { GET_BOOKS } from './actions';

const initialState = {
  books: [],
  bookmarks: []
};

function booksReducer(state = initialState, action) {
  switch (action.type) {
    case GET_BOOKS:
      return { ...state, books: action.payload };
    default:
      return state;
  }
}

export default booksReducer;

Store yapılandırma
Store, actions ve reducers’ları bir araya getiren bir nesnedir. Tek tek bileşenler yerine uygulama düzeyinde state sağlar ve tutar.

redux/ dizini içinde store.js adında yeni bir dosya oluşturun. Redux’da bir store, rootReducer’ı ilk argüman olarak ve bir ara katman yazılımı veya ikinci argüman olarak bir ara katman yazılımı işlevleri koleksiyonunu alan createStore adlı bir işlev kullanılarak oluşturulur.

rootReducer, uygulama genelinde farklı reducer’ların bir kombinasyonudur. Demo uygulamasında sadece bookReducer adında bir reducer bulunmaktadır.

Ara yazılım işlevi thunk, bir redux store’nun, bu demo uygulamasında olduğu gibi bir API URL’sinden veri alma gibi asenkron AJAX istekleri yapmasına izin verir.

Buna aşağıdaki kod parçasını ekleyin:

import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

import booksReducer from './reducers';

const rootReducer = combineReducers({ booksReducer });

export const store = createStore(rootReducer, applyMiddleware(thunk));

Bu Redux store’nu React Native uygulamasında bağlamak için App.js giriş noktası dosyasını açın. İçine, store’u ve High Order Component Provider'i react-redux paketinden içe aktarın. Bu HOC, store’u, artık state’e erişebilen tüm bileşenler gibi uygulamanın geri kalanına aktarmaya yardımcı olur. Ayrıca, tüm ekranlar bu custom navigator’ın çocukları olduğundan, RootNavigator’ı da saracaktır.

App.js dosyasını aşağıda gösterildiği gibi değiştirin:

import React from 'react';
import { Provider } from 'react-redux';

import { store } from './redux/store';
import RootNavigator from './navigation/RootNavigator';

export default function App() {
  return (
    <Provider store={store}>
      <RootNavigator />
    </Provider>
  );
}

API’den veri alma
BooksList.js dosyası, verilerin Temel URL’den getirileceği sekmedir. Aşağıdaki ifadeleri içe aktarın.

import React, { useEffect } from 'react';
import {
  Text,
  View,
  FlatList,
  TouchableOpacity,
  Image,
  SafeAreaView
} from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { MaterialCommunityIcons } from '@expo/vector-icons';

import { getBooks } from '../redux/actions';

Bir Redux store’dan state’e erişmek için useSelector kancası kullanılır. BooksList bileşeninin içinde, kitaplara erişin.

export default function BooksList() {
  const { books } = useSelector(state => state.booksReducer);

  //...
}

Redux store’dan bir action göndermek için useDispatch kancası kullanılır. Kitapları API’den getirmek için getBooks eylemini göndermeniz gerekir. State’e eriştikten sonra aşağıdaki kod parçasını ekleyin.

const dispatch = useDispatch();

const fetchBooks = () => dispatch(getBooks());

useEffect(() => {
  fetchBooks();
}, []);

Ardından, kitap listesini oluşturmak için bir FlatList bileşeniyle dönüş JSX’i ekleyin.

API’den getirilen kitaplar bir dizidir ve verilerin değeri olarak iletilir.

return (
  
    
      Bestsellers
      
         item.id.toString()}
          renderItem={renderItem}
          showsVerticalScrollIndicator={false}
        />
      
    
  
);

renderItem’den döndürülen JSX, listedeki her kitap öğesi için görüntülenecek tüm bilgileri içerir.

Her kitap öğesi şunlara sahip olacak:

Görüntü bileşeni kullanılarak görüntülenen bir kitap kapağı.
Metin bileşeni kullanılarak görüntülenen bir kitap başlığı.
sayfa sayısı ve kitap öğesinin ortalama derecelendirmesi gibi bazı meta bilgiler.
Kitabı BookmarksList ekranına eklemek için dokunulabilir düğme.
Ana dönüş işlevinden hemen önce aşağıdaki renderItem’i ekleyin.

const renderItem = ({ item }) => {
  return (
    
      
        {/* Book Cover */}
        
        {/* Book Metadata */}
        
          {/* Book Title */}
          
            
              {item.title}
            
          
          {/* Meta info */}
          
            
            
              {item.num_pages}
            
            
            
              {item.rating}
            
          
          {/* Buttons */}
          
             console.log('Bookmarked!')}
              activeOpacity={0.7}
              style={{
                flexDirection: 'row',
                padding: 2,
                backgroundColor: '#2D3038',
                borderRadius: 20,
                alignItems: 'center',
                justifyContent: 'center',
                height: 40,
                width: 40
              }}
            >
              
            
          
        
      
    
  );
};

İşte bu adımdan sonra alacağınız çıktı:

Action creators ekleyin ve reducer’ı güncelleyin
redux/actions.js dosyasına, bookmarks kullanıcı tarafından eklendiğinde veya kaldırıldığında durumu güncelleyecek iki action creators daha ekleyelim. Her action creator, daha önce tanımladığımız eylem türünü temel alacaktır. Ayrıca, her action creator, bookmark listesine eklenen kitap öğesini kabul edecektir.

export const addBookmark = book => dispatch => {
  dispatch({
    type: ADD_TO_BOOKMARK_LIST,
    payload: book
  });
};

export const removeBookmark = book => dispatch => {
  dispatch({
    type: REMOVE_FROM_BOOKMARK_LIST,
    payload: book
  });
};

Bir sonraki adım, redux store’un state’ini güncellemektir. redux/reducers.js dosyasını açın ve yeni eklediğimiz eylemleri gerçekleştirmek için aşağıdaki kod parçasını değiştirin.

import {
  GET_BOOKS,
  ADD_TO_BOOKMARK_LIST,
  REMOVE_FROM_BOOKMARK_LIST
} from './actions';

const initialState = {
  books: [],
  bookmarks: []
};

function booksReducer(state = initialState, action) {
  switch (action.type) {
    case GET_BOOKS:
      return { ...state, books: action.payload };
    case ADD_TO_BOOKMARK_LIST:
      return { ...state, bookmarks: [...state.bookmarks, action.payload] };
    case REMOVE_FROM_BOOKMARK_LIST:
      return {
        ...state,
        bookmarks: state.bookmarks.filter(book => book.id !== action.payload.id)
      };
    default:
      return state;
  }
}

export default booksReducer;

Redux persist’ı yapılandırın ve entegre edin
Kalıcı bir reducer oluşturmak için aşağıdaki ifadeleri redux/store.js dosyasına aktarın.

import AsyncStorage from '@react-native-async-storage/async-storage';
import { persistStore, persistReducer } from 'redux-persist';

Ardından, aşağıdaki özelliklere sahip bir persistConfig nesnesi ekleyin:

const persistConfig = {
  key: 'root',
  storage: AsyncStorage,
  whitelist: ['bookmarks']
};

Yukarıdaki snippet’te, kalıcı bir reducer için yapılandırma oluşturmak için key ve storage gereklidir. Depolama, verileri kaydetmek ve sürdürmek için kullanılan depolama motorunun değerine sahiptir. React Native’de, depolamanın değerini açıkça iletmek esastır. Mevcut demo uygulamasında AsyncStorage’ı kullanalım.

Whitelist bir dizi dize alır. Verileri kaydetmek için başlangıç durumundan hangi nesne anahtarının kullanılacağını tanımlamak için kullanılır. Whitelist sağlanmazsa, redux hem kitaplarda hem de bookmark’da kalır. Bookmark’ı Whitelist’in değeri olarak sağlamak, yalnızca bookmark dizisindeki (şu anda boş olan ancak daha sonra bir bookmark eklendiğinde veya kaldırıldığında doldurulacak) verileri kaydedecektir.

Ardından, rootReducer’ı iki bağımsız değişkenle kalıcı reducer ile güncelleyin: persistConfig ve bookReducer.

Ayrıca, kalıcılığı dışa aktarın. Orijinal store’u saran persistStore tarafından döndürülen bir nesnedir.

const rootReducer = combineReducers({
  booksReducer: persistReducer(persistConfig, booksReducer)
});

export const store = createStore(rootReducer, applyMiddleware(thunk));
export const persistor = persistStore(store);

React Native uygulamalarında, root bileşeni PersistGate ile sarmanız gerekir. Bu bileşen, kalıcı state alınana ve redux’a kaydedilene kadar uygulamanın kullanıcı arabiriminin oluşturulmasını geciktirir.

Redux-persist kitaplığından PersistGate’i içe aktarın ve App.js dosyasındaki redux/store dosyasından kalıcılığı içe aktarın:

// Add
import { PersistGate } from 'redux-persist/integration/react';

// Modify to add persistor
import { store, persistor } from './redux/store';

// Then, modify the JSX returned from App component
// Wrap the root component with PersistGate
return (
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <RootNavigator />
    </PersistGate>
  </Provider>
);

Redux-persist kitaplığını yapılandırmak ve React Native ve Redux uygulamasına entegre etmek budur.

v eklemek veya kaldırmak için işlevsellik oluşturun
Tüm kitap öğeleri, API’den alınan BooksList.js dosyasında gösterilir. Bir kullanıcının bir kitap öğesine yer imi ekleyebileceği veya kaldırabileceği sekme ekranındandır.

Diğer action creators’ı da içe aktararak başlayalım:

// Modify
import { getBooks, addBookmark, removeBookmark } from '../redux/actions';

BookReducer, state’e erişmek için kullanılır. Bookmark dizisine erişmek için değiştirin:

const { books, bookmarks } = useSelector(state => state.booksReducer);

Şimdi useDispatch kancasını kullanarak iki eylemi gönderin ve işleyici işlevlerini oluşturun. Bu işleyici işlevleri, kullanıcı tarafından dokunulabilir bileşene basıldığında tetiklenecektir. Her işleyici işlevi bir argümanı kabul edecek ve bu, FlatList’teki mevcut kitap öğesidir.

const addToBookmarkList = book => dispatch(addBookmark(book));
const removeFromBookmarkList = book => dispatch(removeBookmark(book));

const handleAddBookmark = book => {
  addToBookmarkList(book);
};

const handleRemoveBookmark = book => {
  removeFromBookmarkList(book);
};

Tetiklenen eyleme bağlı olarak uygulamanın kullanıcı arayüzünü dinamik olarak değiştirecek ifExists adlı başka bir işleyici işlevi ekleyelim. Bu işlev, dizide (AsyncStorage’da depolanan) bir kitap öğesinin zaten var olup olmadığına bağlı olarak kullanıcı arabiriminde değişiklikler yapmak için bookmark dizisindeki filtreyi kullanacaktır.

const ifExists = book => {
  if (bookmarks.filter(item => item.id === book.id).length > 0) {
    return true;
  }

  return false;
};

Bookmark listesine bir öğe eklemek veya listeden bir öğe kaldırmak için bir eylem tetiklendiğinde uygulamanın kullanıcı arayüzünü dinamik olarak değiştirmek için TouchableOpacity bileşenini değiştirin.

<TouchableOpacity
  onPress={() =>
    ifExists(item) ? handleRemoveBookmark(item) : handleAddBookmark(item)
  }
  activeOpacity={0.7}
  style={{
    // rest remains same
    backgroundColor: ifExists(item) ? '#F96D41' : '#2D3038'
    //
  }}
>
  <MaterialCommunityIcons
    color={ifExists(item) ? 'white' : '#64676D'}
    size={24}
    name={ifExists(item) ? 'bookmark-outline' : 'bookmark'}
  />
</TouchableOpacity>

Bookmark göster
BookmarksList.js sekmesinde bookmark’a eklenen herhangi bir kitap öğesi gösterilecektir. Bookmark’a eklenmiş öğelerin listesini görüntülemenin yanı sıra, kitap öğelerini listeden çıkarma işlevine de sahip olacak.

Aşağıdaki ifadeleri içe aktararak başlayın. Bu sefer, yalnızca removeBookmark eylem oluşturucuyu içe aktarın.

import React from 'react';
import {
  SafeAreaView,
  Text,
  View,
  FlatList,
  TouchableOpacity,
  Image
} from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { MaterialCommunityIcons } from '@expo/vector-icons';

import { removeBookmark } from '../redux/actions';

useSelector kancasını kullanmak,bookmark durumuna erişmemizi sağlar. Ardından, useDispatch kancasını kullanmak, bir kitabı bookmark listesinden çıkarmak için eylem oluşturucu ve işleyici işlevini tanımlar.

export default function BookmarksList() {
  const { bookmarks } = useSelector(state => state.booksReducer);
  const dispatch = useDispatch();

  const removeFromBookmarkList = book => dispatch(removeBookmark(book));

  const handleRemoveBookmark = book => {
    removeFromBookmarkList(book);
  };

  //...
}

Son olarak, bu sekme ekranının kullanıcı arayüzü BooksList.js sekmesine benzer olacaktır. FlatList bileşenini kullanarak, işaretlenmiş tüm öğelerin listesini gösterelim.

İşaretlenmiş hiçbir öğe yoksa, bunu iletmek için basit bir mesaj gösterelim. Bu, durumdan bookmark dizisinin uzunluğunu kontrol ederek yapılır.

BookmarksList sekme bileşeni tarafından döndürülen tam JSX:

export default function BookmarksList() {
  // ...
  const renderItem = ({ item }) => {
    return (
      <View style={{ marginVertical: 12 }}>
        <View style={{ flexDirection: 'row', flex: 1 }}>
          {/* Book Cover */}
          <Image
            source={{ uri: item.image_url }}
            resizeMode='cover'
            style={{ width: 100, height: 150, borderRadius: 10 }}
          />
          {/* Book Metadata */}
          <View style={{ flex: 1, marginLeft: 12 }}>
            {/* Book Title */}
            <View>
              <Text style={{ fontSize: 22, paddingRight: 16, color: 'white' }}>
                {item.title}
              </Text>
            </View>
            {/* Meta info */}
            <View
              style={{
                flexDirection: 'row',
                marginTop: 10,
                alignItems: 'center'
              }}
            >
              <MaterialCommunityIcons
                color='#64676D'
                name='book-open-page-variant'
                size={20}
              />
              <Text style={{ fontSize: 14, paddingLeft: 10, color: '#64676D' }}>
                {item.num_pages}
              </Text>
              <MaterialCommunityIcons
                color='#64676D'
                name='star'
                size={20}
                style={{ paddingLeft: 16 }}
              />
              <Text style={{ fontSize: 14, paddingLeft: 10, color: '#64676D' }}>
                {item.rating}
              </Text>
            </View>
            {/* Buttons */}
            <View style={{ marginTop: 14 }}>
              <TouchableOpacity
                onPress={() => handleRemoveBookmark(item)}
                activeOpacity={0.7}
                style={{
                  flexDirection: 'row',
                  padding: 2,
                  backgroundColor: '#2D3038',
                  borderRadius: 20,
                  alignItems: 'center',
                  justifyContent: 'center',
                  height: 40,
                  width: 40
                }}
              >
                <MaterialCommunityIcons
                  color='#64676D'
                  size={24}
                  name='bookmark-remove'
                />
              </TouchableOpacity>
            </View>
          </View>
        </View>
      </View>
    );
  };

  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: '#1E1B26' }}>
      <View style={{ flex: 1, paddingHorizontal: 16 }}>
        <Text style={{ color: 'white', fontSize: 22 }}>Bookmarks</Text>
        <View style={{ flex: 1, marginTop: 8 }}>
          {bookmarks.length === 0 ? (
            <Text style={{ color: '#64676D', fontSize: 18 }}>
              Add a book to bookmark list.
            </Text>
          ) : (
            <FlatList
              data={bookmarks}
              keyExtractor={item => item.id.toString()}
              renderItem={renderItem}
              showsVerticalScrollIndicator={false}
            />
          )}
        </View>
      </View>
    </SafeAreaView>
  );
}

Uygulamayı Çalıştırmak
Expo istemcisini çalıştırdığınız simülatöre veya gerçek cihaza gidin ve bir öğeye bookmark ekleyerek veya kaldırarak işlevselliği test edebilirsiniz. Ayrıca, ilk sekmedeki bookmark düğmesinin dinamik kullanıcı arayüzü değişikliklerine dikkat edin.

Expo istemcisini kapattığınızdan emin olun ve ardından Redux store’daki durumun devam edip etmediğini görmek için başlatın.

Ve bu kadar! Umarım bu makaleyi faydalı bulmuşsunuzdur.

Kaynak:https://blog.jscrambler.com/

Tavsiye Edilen Yazılar

1 Yorum

  1. Mükemmel bir içerik favorilerim tarzı bir şey yapmaya çalışıyordum.


xcodepers için bir cevap yazın Cevabı iptal et

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir