0

It might sound silly but I am just learning here I am trying to convert a component to a function-based component. I did everything right but I am stuck on something very silly

I have this code for Discover

export default class Discover extends React.PureComponent {
  state = {
    items: [],
  };
    cellRefs: {};

  constructor(props) {
    super(props);
    this.cellRefs = {};
  }

what is the correct way to convert cellRefs to work with the function I have? I tried everything when I do this in my class file it is fine it gives me an object with the things I need.

const cell = this.cellRefs[item.key];

However,

const cell = cellRefs[item.key];

is just giving undefined

Full code for the converted component

import React, { useState, useEffect, useRef, Children } from 'react';
import {
  StyleSheet,
  Text,
  View,
  FlatList,
  Dimensions,
  TouchableOpacity,
  Image,
} from 'react-native';
import { Video } from 'expo-av';




const { height, width } = Dimensions.get('window');

const cellHeight = height * 0.6;
const cellWidth = width;

const viewabilityConfig = {
itemVisiblePercentThreshold: 80,
};



class Item extends React.PureComponent {
  video: any;
  componentWillUnmount() {
    if (this.video) {
      this.video.unloadAsync();
    }
  }

  async play() {
    const status = await this.video.getStatusAsync();
    if (status.isPlaying) {
      return;
    }
    return this.video.playAsync();
  }

  pause() {
    if (this.video) {
      this.video.pauseAsync();
    }
  }

  render() {
    const { id, poster, url } = this.props;
    const uri = url + '?bust=' + id;
    return (
      <View style={styles.cell}>
        <Image
          source={{
            uri: poster,
            cache: 'force-cache',
          }}
          style={[styles.full, styles.poster]}
        />
        <Video
          ref={ref => {
            this.video = ref;
          }}
          source={{ uri }}
          shouldPlay={false}
          isMuted
          isLooping
          resizeMode="cover"
          style={styles.full}
        />
        <View style={styles.overlay}>
          <Text style={styles.overlayText}>Item no. {id}</Text>
          <Text style={styles.overlayText}>Overlay text here</Text>
        </View>
      </View>
    );
  }
}







interface FeedListProps {

}

export const FeedList: React.FC<FeedListProps> = (props) => {
  const initialItems = [
    {
      id: 1,
      url: 'https://s3.eu-west-2.amazonaws.com/jensun-uploads/shout/IMG_1110.m4v',
      poster:
        'https://s3.eu-west-2.amazonaws.com/jensun-uploads/shout/norwaysailing.jpg',
    },
    {
      id: 2,
      url:
        'https://s3.eu-west-2.amazonaws.com/jensun-uploads/shout/croatia10s.mp4',
      poster:
        'https://s3.eu-west-2.amazonaws.com/jensun-uploads/shout/croatia10s.jpg',
    },
  ];
  const [items, setItems] = useState([]);
  //this.cellRefs = {};
  //let cellRefs: {};
  //cellRefs= {};
  const cellRefs = React.useRef({})
  const viewConfigRef = React.useRef({ itemVisiblePercentThreshold: 80 })
 

  useEffect(() => {
    loadItems();
    setTimeout(loadItems, 1000);
    setTimeout(loadItems, 1100);
    setTimeout(loadItems, 1200);
    setTimeout(loadItems, 1300);
  }, []);

  const _onViewableItemsChanged = React.useRef((props)=>{
    const changed = props.changed;
    changed.forEach(item => {
      const cell = cellRefs[item.key];
      
      console.log("CALLING IF"+ cell + "        " + item.key)
      if (cell) {
        if (item.isViewable) {
          console.log("PLAY OS CALLED")
          cell.play();
        } else {
          console.log("Pause is played")
          cell.pause();
        }
      }
    });
  });

  function loadItems(){

    const start = items.length;

    const newItems = initialItems.map((item, i) => ({
      ...item,
      id: start + i,
    }));

    const Litems = [...items, ...newItems];

    setItems( Litems );

  };

  function _renderItem({ item }){
    return (
      <Item
        ref={cellRefs[item.id]}
        {...item}
      />
    );
  };



    return (

      <View style={styles.container}>
        <FlatList
          style={{ flex: 1 }}
          data={items}
          renderItem={_renderItem}
          keyExtractor={item => item.id.toString()}
          onViewableItemsChanged={_onViewableItemsChanged.current}
          initialNumToRender={3}
          maxToRenderPerBatch={3}
          windowSize={5}
          getItemLayout={(_data, index) => ({
            length: cellHeight,
            offset: cellHeight * index,
            index,
          })}
          viewabilityConfig={viewabilityConfig}
          removeClippedSubviews={true}
          ListFooterComponent={
            <TouchableOpacity onPress={loadItems}>
              <Text style={{ padding: 30 }}>Load more</Text>
            </TouchableOpacity>
          }
        />
      </View>
    );
}



const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  cell: {
    width: cellWidth - 20,
    height: cellHeight - 20,
    backgroundColor: '#eee',
    borderRadius: 20,
    overflow: 'hidden',
    margin: 10,
  },
  overlay: {
    position: 'absolute',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    backgroundColor: 'rgba(0,0,0,0.4)',
    padding: 40,
  },
  full: {
    position: 'absolute',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
  },
  poster: {
    resizeMode: 'cover',
  },
  overlayText: {
    color: '#fff',
  },
});
Fares K Saleh
  • 37
  • 1
  • 8

2 Answers2

0

It would be best if you could inline your source code into stack overflow, than keeping it in a separate link. However I took a look at the component, and I think the issue is with how you are using props.

On line 91,

export const FeedList: React.FC<FeedListProps> = ({}) => {

So here, you need to get the props as an argument. Earlier with the class component it was available at this.props. However here you need to pass it as below,

export const FeedList: React.FC<FeedListProps> = (props) => {

Or you may destructure the props as below,

export const FeedList: React.FC<FeedListProps> = ({changed}) => {

You will also need to modify the interface on line 87 to reflect the type.

Kenny John Jacob
  • 1,188
  • 8
  • 21
  • I did update the code still the same issue I am getting undefined. I did lots of debugging here and there the issue is that cellRefs is not referring to the correct place. So when I console cellRefs in the working code "class" I get that it is an object but for the function code, I get undefined. Another thing is I have zero idea how to set up the interface, I am learning react since like 3 days go so... help :) – Fares K Saleh Aug 25 '20 at 12:04
0

Should be enough to use a functional component and use useRef([]) initialized as an array

Working example: https://snack.expo.io/2_xrzF!LZ

Stevetro
  • 1,933
  • 3
  • 16
  • 29
  • wow I mean I was so close just my lack of experience I some small mistakes here and there more of syntax. Thank you so much I will spend the rest of the day studying the changes to understand what is going on. I would be glad if you can even explain what you did on the fly :) – Fares K Saleh Aug 25 '20 at 12:16
  • I saw your paste too late, thats why i changed it by my own. From that point of view there was not much wrong with your code. What I changed: Exporting the app as default, assigning the reference in _renderItem {ref => cellRefs[item.id] = ref } and initializing useRef as array React.useRef([]) – Stevetro Aug 25 '20 at 13:59
  • i marked the changes in your code with //changed https://snack.expo.io/SN1F_lQCb – Stevetro Aug 25 '20 at 14:01
  • I used your code on my local machine and it did not work since your answer I spend all the time trying to find out why. Something very strange happened when I "download" you code and run it from expo cli it never works. Online your code and even my old code works just fine. Any tip pleaseee :)? – Fares K Saleh Aug 25 '20 at 14:12