2021年4月5日星期一

How can I improve performance of this React Native code?

I am working on React Native app of mine and I have a screen where I am using RN Cameraroll and getting all of the images and allow user to select n number of images. I am marking images that are selected by user and marking them in order. When I was writing code of the screen, I used Android emulator and it worked just fine. But when I tested it in my Samsung s7 device (one of old gen phone that I have as test device) it didn't work as expected. So the view gets updated and it renders conditional view where it blurs out image and displays that image's number in order. Here is gif file of it,

image

As you can see, it is rendering numbers and all but it is slow. Now this works slowly in this device specially. I have couple other Android devices that I have tested this screen but they are newer devices and faster devices so they work better. Not smooth as my emulator but they work okay. So there is performance issue but I guess if I know how to fix it for this device, all devices performance will get fixed.

I am using useState() hook to store order of images path in array. so here is my code,

  const SCREEN_WIDTH = Math.floor(Dimensions.get('window').width);  const PADDING = Math.floor(SCREEN_WIDTH * 0.015);  const RENDER_IMAGES_PER_ROW = SCREEN_WIDTH >= 500 ? 5 : 4;  const IMAGES_ROWS = 8;  const FETCH_IMAGES_PER_REQUEST = RENDER_IMAGES_PER_ROW * IMAGES_ROWS;  const WH = Math.floor((SCREEN_WIDTH - PADDING) / RENDER_IMAGES_PER_ROW);  const IMAGE_WIDTH = WH;  const IMAGE_HEIGHT = WH;        const initialImagesInOrder: string[] = [];      const [imagesInOrder, setImagesInOrder] = useState(initialImagesInOrder);      const [media, setMedia] = useState([]);      const [mediaToRender, setMediaToRender] = useState([]);      const [after, setAfter] = useState('');      const [checkedForMedia, setCheckedForMedia] = useState(false);      const [initialRequest, setInitialRequest] = useState(true);      const [hasNextPage, setHasNextPage] = useState(true);      const [videoURI, setVideoURI] = useState('');          const hasAndroidPermission = async () => {          const permission =              PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;            const hasPermission = await PermissionsAndroid.check(permission);          if (hasPermission) {              return true;          }            const status = await PermissionsAndroid.request(permission);          return status === 'granted';      };        const permissionCheck = async () => {          if (Platform.OS === 'android' && !(await hasAndroidPermission())) {              return;          }      };        const getLocalMedia = (_after: string | null) => {          if (hasNextPage) {              let params = {                  first: FETCH_IMAGES_PER_REQUEST,                  assetType: props.mediaType,                  include: ['playableDuration'],              };                if (_after !== null) {                  // @ts-ignore                  params.after = _after;              }                // @ts-ignore              CameraRoll.getPhotos(params)                  .then(r => {                      // @ts-ignore                      const newImages = media.concat(r.edges);                        setCheckedForMedia(true);                      setInitialRequest(false);                        // @ts-ignore                      setMedia(newImages);                        // @ts-ignore                      setAfter(r.page_info.end_cursor);                      // @ts-ignore                      setHasNextPage(r.page_info.has_next_page);                  })                  .catch(error => {                      modalData.modalText = `Unable to load media!`;                      modalData.modalIsVisible = true;                      setModalData(modalData);                                        });          }      };        useEffect(() => {          permissionCheck()              .then(() => {                  setCheckedForMedia(false);                  setMedia([]);                  setInitialRequest(true);                  setHasNextPage(true);                  setImagesInOrder(initialImagesInOrder);                  setVideoURI('');              })              .catch(error => {                  modalData.modalText = `Something went wrong! Please try again.`;                  modalData.modalIsVisible = true;                  setModalData(modalData);                });      }, [props]);        useEffect(() => {          if (              media.length === 0 &&              !checkedForMedia &&              initialRequest &&              hasNextPage          ) {              getLocalMedia(null);          }      }, [media, checkedForMedia, initialRequest, hasNextPage]);        useEffect(() => {          setMediaToRender(              media.reduce((all, one, i) => {                  const ch = Math.floor(i / RENDER_IMAGES_PER_ROW);                  // @ts-ignore                  all[ch] = [].concat(all[ch] || [], one);                  return all;              }, []),          );      }, [media]);          const handleImage = (imageURI: string) => {          if (imagesInOrder.includes(imageURI)) {              setImagesInOrder(imagesInOrder.filter(i => i !== imageURI));          } else {              if (                  props.maxImagesPerPost !== undefined &&                  props.maxImagesPerPost !== null &&                  imagesInOrder.length < props.maxImagesPerPost              ) {                  setImagesInOrder([...imagesInOrder, imageURI]);              }          }      };        // @ts-ignore      const renderItem = ({ item }) => {          if (props.mediaType === 'Photos') {              return renderImages(item);          } else {              return renderVideos(item);          }      };        const renderImageBackground = ({          uri,          data,          seconds,      }: {          uri: string;          data: any | null;          seconds: number | null;      }) => (          <ImageBackground              style={styles.imageBackgroundStyle}              source=              resizeMode="cover"              key={uri}>              {data !== undefined && data !== null && (                  <View style={styles.imageBackgroundChild}>                      <View style={styles.imageSelectedView}>{data}</View>                  </View>              )}              {data == null && seconds !== null && (                  <View                      style={                          props.maxVideoLengthInSeconds !== undefined &&                          seconds > props.maxVideoLengthInSeconds                              ? {                                      ...styles.secondsViewForVideo,                                      ...styles.videoDisabled,                                }                              : {                                      ...styles.secondsViewForVideo,                                }                      }>                      <Text                          style={                              props.maxVideoLengthInSeconds !== undefined &&                              seconds > props.maxVideoLengthInSeconds                                  ? styles.disabledSecondsTextForVideo                                  : styles.secondsTextForVideo                          }>                          {formatSecondsForVideo(seconds.toString())}                      </Text>                  </View>              )}          </ImageBackground>      );        const renderImages = (item: any) => (          <View style={styles.mediaView}>              {                  // @ts-ignore                  item.map(i => {                      const uri = i.node.image.uri;                      const data = imagesInOrder.includes(uri) ? (                          <Text style={styles.imageSelectedText}>                              {imagesInOrder.indexOf(uri) + 1}                          </Text>                      ) : null;                      const seconds = null;                        return (                          <View                              style={styles.mediaSubView}                              key={Helper.generateRandomKey()}>                              <TouchableOpacity                                  onPress={() => {                                      handleImage(uri);                                  }}>                                  {renderImageBackground({                                      uri,                                      data,                                      seconds,                                  })}                              </TouchableOpacity>                          </View>                      );                  })              }          </View>      );        const handleMedia = () => {          if (checkedForMedia && media.length === 0) {              return (                  <View style={[styles.containerForNoImage]}>                      <Text style={[styles.text]}>                          No {props.mediaType === 'Photos' ? `photo` : `video`}{' '}                          found!                      </Text>                  </View>              );          }            return (              <FlatList                  data={mediaToRender}                  keyExtractor={(item, index) => index.toString()}                  // @ts-ignore                  renderItem={renderItem}                  initialNumToRender={5}                  maxToRenderPerBatch={10}                  windowSize={10}                  onEndReachedThreshold={0.5}                  onEndReached={() => {                      getLocalMedia(after);                  }}              />          );      };    

So my logic is straight forward. Add or remove image from state array and update rendered image if its path exist in array. If path exist, get index and update the view. But I am not sure why it is not performing fast enough in my test device! The order array is not crazy big that should cause problem. As you can see in gif that blur view gets rendered right away but text in middle doesn't render with it.

Also, I have shared only chunk of code that handles images stuff! If I have missed something, please let me know and I will update code! Also when I scroll images or this view first gets rendered, it loads images slowly! Any suggestion to load images faster? They are locally stored on device and not getting fetched from remote server!

Thanks in advance for you help!

https://stackoverflow.com/questions/66947440/how-can-i-improve-performance-of-this-react-native-code April 05, 2021 at 09:55AM

没有评论:

发表评论