پیاده‌سازی یک لیست اسکرول بی نهایت در React Native
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 6 دقیقه

پیاده‌سازی یک لیست اسکرول بی نهایت در React Native

وقتی که در حال پیاده‌سازی صفحه‌بندی‌ها در یک دستگاه موبایل هستید، با توجه به این که فضای موجود بر خلاف وب در حالت حداقلی خود است، اسکرول کردن بی نهایت همیشه راه حل مد نظر همه بوده است که یک تجربه نرم و مطلوب را به کاربران شما می‌دهد.

در این آموزش، ما یک لیست اسکرول بی نهایت با استفاده از کامپوننت FlatList در React Native خواهیم ساخت، و همچنین اِی‌پی‌آی Punk را در بر خواهیم گرفت که در واقع یک API کاتالوگ نوشیدنی رایگان است.

جدول محتوا:

  • راه‌اندازی
  • ساختار شاخه
  • پیکربندی Axios
  • استایل‌بندی کارت‌ها
  • کامپوننت FlatList
  • نتیجه گیری

راه‌اندازی

ما از create-react-native-app برای bootstrap کردن برنامه React Native خود استفاده خواهیم کرد. این دستور را برای نصب آن به صورت global اجرا کنید:

npm install -g create-react-native-app

سپس باید برنامه را در شاخه ارجع خود bootstrap کنید:

react-native init react_native_infinite_scroll_tutorial

من از یک شبیه‌ساز اندروید برای این آموزش استفاده خواهم کرد، اما کد مربوطه هم برای پلتفرم IOS و هم برای اندروید کار خواهد کرد.

مطمئن شوید که شبیه‌ساز شما آماده به کار است و سپس به شاخه پروژه خود رفته، و این دستور را اجرا کنید:

react-native run-android

این دستور باید تمام dependencyهای مورد نیاز را دانلود کرده، برنامه را بر روی شبیه‌ساز شما باز کرده، و سپس آن را به طور خودکار اجرا کند. شما بید یک صفحه با متن پیشفرض را به این صورت ببینید:

دقت کنید که ما برنامه نمونه خود را نصب کرده، و اجرا کرده‌ایم. حال ما dependencyهای مورد نیاز برای پروژه را نصب خواهیم کرد. ما از Axios برای ارسال درخواست به سرور و Glamorous Native برای استایل‌بندی کامپوننت خود استفاده خواهیم کرد. این دستور را برای نصب آن‌ها اجرا کنید:

npm install -S axios glamorous-native

ساختار شاخه

ساختار شاخه همیشه برای یک برنامه ضروری است. با توجه به این که این برنامه یک برنامه دمو و ساده است، ما آن را در کمترین حد ممکن نگه خواهیم داشت:

src
├── App.js
├── components
│   ├── BeerPreviewCard
│   │   ├── BeerPreviewCard.js
│   │   └── index.js
│   ├── ContainedImage
│   │   └── index.js
│   └── Title
│       └── index.js
├── config
│   └── theme.js
└── utils
    └── lib
        └── axiosService.js

پیکربندی Axios

در جهت این که استفاده از Axios را برای خود آسان‌تر کنیم، یک نمونه تکی از خدمات Axios خواهیم ساخت که می‌توانیم در کامپوننت خود وارد کنیم:

import axios from 'axios';

const axiosService = axios.create({
  baseURL: 'https://api.punkapi.com/v2/',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
});

// singleton instance
export default axiosService;

استایل‌بندی کارت‌ها

سپس، ما کارت‌هایی را برای نمایش داده‌های نوشیدنی خود خواهیم ساخت، و مقداری طراحی هم به آن‌ها اضافه خواهیم کرد.

theme.js

این فایل شامل بوم رنگ برنامه ما خواهد بود که در برنامه از آن استفاده خواهیم کرد.

export const colors = {
  french_blue: '#3f51b5',
  deep_sky_blue: '#007aff',
  white: '#ffffff',
  black: '#000000',
  veryLightPink: '#f2f2f2'
};

title.js

این فایل شامل کامپوننت متن کارت خواهد بود که ما از آن برای نمایش نام نوشیدنی در کارت استفاده خواهیم کرد.

import glamorous from 'glamorous-native';
import { colors } from '../../config/theme';

const Title = glamorous.text((props, theme) => ({
  fontFamily: 'robotoRegular',
  fontSize: 16,
  color: props.color || colors.black,
  lineHeight: 24,
  textAlign: props.align || 'left',
  alignSelf: props.alignSelf || 'center'
}));

export default Title;

ContainedImage.js

این فایل شامل کامپوننت تصویر ما می‌باشد، که یک resizeMode را به همراه خواهد داشت تا بتوانیم تصویر را در کامپوننت محصور آن قرار دهیم.

import React from 'react';
import glamorous from 'glamorous-native';

const CardImageContainer = glamorous.view((props, theme) => ({
  flex: 1,
  alignItems: 'stretch'
}));

const StyledImage = glamorous.image((props, theme) => ({
  position: 'absolute',
  top: 0,
  left: 0,
  bottom: 0,
  right: 0
}));

const ContainedImage = props => {
  return (
    <CardImageContainer>
      <StyledImage resizeMode="contain" {...props} />
    </CardImageContainer>
  );
};

export default ContainedImage;

BeerPreviewCard.js

این فایل شامل محفظه کارت اصلی خواهد بود. این فایل، جایی است که ما کامپوننت عنوان و کامپوننت تصویر را ترکیب می‌کنیم، تا یک کارت را تشکیل دهیم که نام نوشیدنی و تصویر آن را نمایش می‌دهد.

import React from 'react';
import glamorous from 'glamorous-native';

// app theme colors
import { colors } from '../../config/theme';

// components
import Title from '../Title';
import ContainedImage from '../ContainedImage';

const CardContainer = glamorous.view((props, theme) => ({
  height: 160,
  width: '85%',
  left: '7.5%',
  justifyContent: 'space-around'
}));

const CardImageContainer = glamorous.view((props, theme) => ({
  flex: 1,
  alignItems: 'stretch'
}));

const BeerNameContainer = glamorous.view((props, theme) => ({
  height: '30%',
  backgroundColor: colors.deep_sky_blue,
  justifyContent: 'center'
}));

const BeerPreviewCard = ({ name, imageUrl }) => {
  return (
    <CardContainer>
      <CardImageContainer>
        <ContainedImage source={{ uri: imageUrl }} />
      </CardImageContainer>
      <BeerNameContainer>
        <Title align="center" color={colors.white}>
          {name}
        </Title>
      </BeerNameContainer>
    </CardContainer>
  );
};

export default BeerPreviewCard;

دریافت نوشیدنی‌ها

منطق دریافت نوشیدنی‌ها در فایل App.js خواهد بود، که کامپوننت اصلی برنامه ما می‌باشد. ما باید API را با ارسال یک درخواست GET به کار بگیریم تا یک لیست از نوشیدنی‌های صفحه‌بندی شده را دریافت کنیم:

import React, { Component } from 'react';

// axios service
import axiosService from './utils/lib/axiosService';

export default class AllBeersScreen extends Component {
  state = {
    data: [],
    page: 1,
    loading: true,
    error: null
  };

  componentDidMount() {
    this._fetchAllBeers();
  }

  _fetchAllBeers = () => {
    const { page } = this.state;
    const URL = `/beers?page=${page}&per_page=10`;

    axiosService
      .request({
        url: URL,
        method: 'GET'
      })
      .then(response => {
        this.setState((prevState, nextProps) => ({
          data:
            page === 1
              ? Array.from(response.data)
              : [...this.state.data, ...response.data],
          loading: false
        }));
      })
      .catch(error => {
        this.setState({ error, loading: false });
      });
  };

 render() {
    return (
        // map through beers and display card
    );
}

کامپوننت FlatList

پس کامپوننت FlatList چیست؟ طبق گفته اسناد React Native، این کامپوننت یک رابط اجرا کننده برای رندر کردن لیست‌های ساده و صاف می‌باشد، که اکثر امکانات کاربردی از جمله این موارد را پشتیبانی می‌کند:

  • کاملا میان پلتفرمی.
  • حالت افقی دلخواه.
  • Callbackهای view قابل پیکربندی.
  • پشتیبانی از header.
  • پشتیبانی از footer.
  • پشتیبانی از separator.
  • Pull to Refresh.
  • بارگذاری اسکرول.
  • پشتیبانی از ScrollTolndex.

ما از چند ویژگی از لیست بالا، از جمله footer، pull to refresh و بارگذاری اسکرول برای برنامه خود استفاده خواهیم کرد.

به کار گیری پایه:

برای استفاده از کامپوننت FlatList، شما باید دو prop اصلی را منتقل کنید که، در  این propها برابر با RenderItem و Data هستند. حال ما می‌توانیم داده‌هایی که پیش‌تر در کامپوننت FlatList دریافت شده‌اند را منتقل کنیم و از کامپوننت BeerPreviewCard برای رندر کردن یک FlatList ساده به این صورت استفاده کنیم:

export default class AllBeersScreen extends Component {
 // fetch beer request and update state from earlier on
 render() {
    return (
         <FlatList
          contentContainerStyle={{
            flex: 1,
            flexDirection: 'column',
            height: '100%',
            width: '100%'
          }}
          data={this.state.data}
          keyExtractor={item => item.id.toString()}
          renderItem={({ item }) => (
            <View
              style={{
                marginTop: 25,
                width: '50%'
              }}
            >
              <BeerPreviewCard name={item.name} imageUrl={item.image_url} />
            </View>
          )}
        />
    );
}

برنامه خود را مجددا بارگذاری کنید، و باید چنین چیزی را ببینید:

بارگذاری اسکرول

ویژگی اصلی اسکرول کردن بی نهایت، بارگذاری محتویات در هنگام تمایل و همینطور که کاربر برنامه را اسکرول می‌کند، می‌باشد. برای رسیدن به این هدف، کامپوننت FlatList باید دو prop اصلی onEndReached و onEndReachedThreshold را داشته باشد.

onEndReached یک callback است که وقتی موقعیت اسکرول کاربر به onEndReachedThreshold محتویات رندر شده نزدیک است، فراخوانی می‌شود. onEndReachedThreshold اساسا یک عدد است که موقعیت اسکرول کاربر را نسبت به فاصله او از انتهای محتویات قابل مشاهده نمایش می‌دهد. وقتی که کاربر به انتهای موقعیت مشخص شده می‌رسد، callback با نام onEndReached فعال می‌شود.

مقدار 0.5 وقتی که انتهای محتویات در نیمی از طول لیست است، onEndReached را فعال می‌کند.

export default class AllBeersScreen extends Component {
  state = {
    data: [],
    page: 1,
    loading: true,
    loadingMore: false,
    error: null
  }; 

 // fetch beer request and update state from earlier on

  _handleLoadMore = () => {
    this.setState(
      (prevState, nextProps) => ({
        page: prevState.page + 1,
        loadingMore: true
      }),
      () => {
        this._fetchAllBeers();
      }
    );
  };

 render() {
    return (
         <FlatList
          contentContainerStyle={{
            flex: 1,
            flexDirection: 'column',
            height: '100%',
            width: '100%'
          }}
          data={this.state.data}
          renderItem={({ item }) => (
            <View
              style={{
                marginTop: 25,
                width: '50%'
              }}
            >
              <BeerPreviewCard name={item.name} imageUrl={item.image_url} />
            </View>
          )}
          onEndReached={this._handleLoadMore}
          onEndReachedThreshold={0.5}
          initialNumToRender={10}
        />
    );
  }
}

اگر به برنامه برگردید و به پایین اسکرول کنید، متوجه خواهید شد که لیست نوشیدنی‌ها به طور خودکار و همینطور که به پایین اسکرول می‌کنید، بارگذاری شده است.

Footer

Footer اساسا بخش پایینی کامپوننت FlatList است. وقتی که کاربر به پایین اسکرول می‌کنند، ما می‌خواهیم یک loader نشان دهیم که محتویات به پایان دریافت شده‌اند. ما می‌توانیم با استفاده از یک prop با نام ListFooterComponent به این هدف برسیم، که در آن یک تابع را منتقل خواهیم کرد و این تابع یک کامپوننت ActivityIndicator را به صورت جمع‌بندی شده در یک کامپوننت View بر خواهد گرداند:

  _renderFooter = () => {
    if (!this.state.loadingMore) return null;

    return (
      <View
        style={{
          position: 'relative',
          width: width,
          height: height,
          paddingVertical: 20,
          borderTopWidth: 1,
          marginTop: 10,
          marginBottom: 10,
          borderColor: colors.veryLightPink
        }}
      >
        <ActivityIndicator animating size="large" />
      </View>
    );
  };

 render() {
    return (
         <FlatList
          // other props
          ListFooterComponent={this._renderFooter}
        />
    );
  }

حال وقتی که اسکرول می‌کنید و محتویات در حال بارگذاری هستند، یک loader بر روی صفحه نمایش داده خواهد شد.

Pull To Refresh

عملکرد pull to refresh به طور گسترده در برنامه‌های مدرنی که از فعالیت شبکه برای دریافت داده‌ها استفاده می‌کنند، به کار برده می‌شود. برای رسیدن به این هدف در FlatList، ما باید یک prop با نام onRefresh را منتقل کنیم که وقتی کاربر از بالای صفحه دست خود را به پایین می‌کشد، یک callback را فعال می‌کند:

  _handleRefresh = () => {
    this.setState(
      {
        page: 1,
        refreshing: true
      },
      () => {
        this._fetchAllBeers();
      }
    );
  };

 render() {
    return (
         <FlatList
          // other props
          onRefresh={this._handleRefresh}
          refreshing={this.state.refreshing}
        />
    );
  }

حال وقتی که سعی کنید از بالای صفحه دست خود را به پایین بکشید، یک loader از بالا نمایان شده، و محتویات بارگذاری خواهند شد.

توضیح propهای اضافی:

initialNumToRender - این تعداد آیتم‌هایی است که ما می‌خواهیم وقتی برنامه ما داده‌ها را بارگذاری می‌کند، رندر کنیم.

keyExtractor - این مورد برای استخراج یک کلید منحصر به فرد برای یک آیتم داده شده، در ورودی مشخص شده استفاده می‌شود.

نتیجه گیری

اسکرول کردن بی نهایت، یک تجربه نرم را در هنگام استفاده از برنامه شما برای کاربر فراهم می‌کند، و یک راه ساده برای شما است تا محتویات قابل نمایش را به کاربران خود تحویل دهید.

شما می‌توانید به کد کامل برنامه در این لینک دسترسی داشته باشید.

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@er79ka

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید