I am trying to pass dynamic value to property visible to each item of FlatList if they are inside the viewport, thus value of this prop changes on scroll, but I am getting a bit laggy scrolling experience (items are changing positions on every rerender, something like jumping up and down).
How can I avoid it?
my code looks like
import React, { useState, useRef } from 'react';
import {FlatList} from 'react-native';
const TabOne = ({ data, ...rest }) => {
const [visibleIdx, setVisibleIdx] = useState(null);
const onViewableItemsChanged = useRef(({ changed }) => {
setVisibleIdx(changed.length ? changed[0].item.id : null);
});
const renderItem = (item) => (
<ContentItem isVisible={item.id === visibleIdx} />
);
return (
<FlatList
data={data}
viewabilityConfig={{ viewAreaCoveragePercentThreshold: 75 }}
onViewableItemsChanged={onViewableItemsChanged.current}
renderItem={({ item, index }) => renderItem(item, index)}
{...rest}
/>
);
};
export default TabOne;
Related
enter image description here
this is the set up... using use effect to pull all users from data to display in flat list... console.logging data from all friends and it appears in the terminal... but it will not render in flatList or says undefined when I console.log res.data.fullName etc...
import { View, Text, FlatList } from 'react-native'
import React, { useEffect, useState } from 'react'
import axios from 'axios';
const FriendsList = () => {
const [allFriends, setAllFriends] = useState([]);
console.log(allFriends)
useEffect(() => {
axios.get('http://localhost:3000/api/users')
.then(res => {
console.log(res)
})
.catch(err => console.log(err))
}, [])
return (
<>
<Text>Friends:</Text>
<FlatList
data={allFriends}
keyExtractor={(friend, id)=> id}
renderItem={({ item }) => {
return (
<View>
<Text>{item.fullName }</Text>
</View>
)
}}
/>
</>
)
}
export default FriendsList
I want to prevent unneccessary rerender, so I use useMemo.
But I got this error message:
TypeError: renderItem is not a function. (In 'renderItem(props)', 'renderItem' is an instance of Object)
Code:
import * as React from 'react';
import { StyleSheet, Text, View, TouchableOpacity, Image, Dimensions, FlatList } from 'react-native';
import faker from 'faker';
const { width, height } = Dimensions.get('window');
const Advertising = () => {
const data = [
{ id: '1', name: 'Jens', image: faker.image.avatar() },
{ id: '2', name: 'Günther', image: faker.image.avatar() }
];
const renderItem = React.useMemo(() => {
return (
<View>
<Text>Hello</Text>
</View>
)
}, [data]);
return (
<FlatList
data={data}
keyExtractor={item => Math.random(100).toString()}
renderItem={renderItem}
/>
)
};
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
export default React.memo(Advertising);
......................................................................................................................................................................................................
useMemo is a react hook and react hooks can't be used in that way.
I would advice you create a separate component for the this.
const MyComponent = React.memo(({item})=>{
return (<View></View>);
});
and then import like so
const renderItem = ({item}) => {
return <MyComponent />
}
...
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(_item, i)=>i.toString()}
/>
Also consider useCallBack
You have to return your renderItem function as a callback inside useMemo.
const renderItem = React.useMemo(() => () => (
<View>
<Text>Hello</Text>
</View>
), [data])
same as
const renderItem = () => (
<View>
<Text>Hello</Text>
</View>
)
const memoizedRenderItem = React.useMemo(renderItem, [data])
How to convert Fetch to Axios and Class component to Functional component?
I want to learn to implement Infinite-Scrolling using functional component and axios in React native, but it is difficult to apply because the reference document is composed of class component and fetch.
import React from 'react';
import {
View,
Image,
Text,
FlatList, // here
} from 'react-native';
export default class App extends React.Component {
state = {
data: [],
page: 1 // here
}
_renderItem = ({item}) => (
<View style={{borderBottomWidth:1, marginTop: 20}}>
<Image source={{ uri: item.url }} style={{ height: 200}} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
_getData = () => {
const url = 'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + this.state.page;
fetch(url)
.then(r => r.json())
.then(data => {
this.setState({
data: this.state.data.concat(data),
page: this.state.page + 1
})
});
}
componentDidMount() {
this._getData();
}
// here
_handleLoadMore = () => {
this._getData();
}
render() {
return (
<FlatList
data={this.state.data}
renderItem={this._renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={this._handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
}
When converting from a class to a function component, there are a few steps which are relevant here:
replace lifecycle events like componentDidMount with useEffect.
replace component state with one or many useState hooks.
convert class methods to plain functions.
remove all references to this.
delete render() and just return the JSX directly.
The methods _renderItem, _getData, and _handleLoadMore are basically unchanged. They just become const variables instead of class properties.
Here's the straight conversion from class to function component:
import React, { useEffect, useState } from 'react';
import {
View,
Image,
Text,
FlatList,
} from 'react-native';
export default function App() {
const [page, setPage] = useState(1);
const [data, setData] = useState([]);
const _renderItem = ({ item }) => (
<View style={{ borderBottomWidth: 1, marginTop: 20 }}>
<Image source={{ uri: item.url }} style={{ height: 200 }} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
const _getData = () => {
const url =
'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + page;
fetch(url)
.then((r) => r.json())
.then((data) => {
setData(data.concat(data));
setPage(page + 1);
});
};
const _handleLoadMore = () => {
_getData();
};
// useEffect with an empty dependency array replaces componentDidMount()
useEffect(() => _getData(), []);
return (
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={_handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
Here it is with axios and with a few other improvements. I noticed that the end reached function was being called upon reaching the end of the initial zero-length list causing the first page to be fetched twice. So actually the componentDidMount is not needed. I changed from .then() to async/await, but that doesn't matter.
import React, { useEffect, useState } from 'react';
import { View, Image, Text, FlatList } from 'react-native';
import axios from 'axios';
export default function App() {
const [page, setPage] = useState(1);
const [data, setData] = useState([]);
const _renderItem = ({ item }) => (
<View style={{ borderBottomWidth: 1, marginTop: 20 }}>
<Image source={{ uri: item.url }} style={{ height: 200 }} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
const _getData = async () => {
const url =
'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + page;
const res = await axios.get(url);
setData(data.concat(res.data));
setPage(page + 1);
};
const _handleLoadMore = () => {
_getData();
};
// useEffect with an empty dependency array replaces componentDidMount()
useEffect(() => {
// put async functions inside curly braces to that you aren't returing the Promise
_getData();
}, []);
return (
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={_handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
Expo Link -- It works on my device, but the infinite scroll doesn't seem to work in the web preview.
There are additional more "advanced" improvements that you can make:
set state with a callback of previous state to ensure that values are always correct. setPage(current => current + 1) setData(current => current.concat(res.data))
memoization with useCallback so that functions like _renderItem maintain a constant reference across re-renders.
exhaustive useEffect dependencies (requires memoization).
i cannot make the flatlist stay away from my data entry field which is in its footer. here is my code:
import React, { useState, useEffect } from 'react';
import { View, Text, Alert , TextInput, Button, Platform, KeyboardAvoidingView,Animated,Easing} from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
export function PlayAreaScreen({ route, navigation }) {
const [itemsToShow, setitemsToShow] = React.useState([{key:'0',name:"sdfsdfds"}]);
const PopulateTestData = () =>{
const DATA = [];
for (let index = 0; index < 6; index++) {
DATA.push({key:index.toString(), name:`index ${index}`});
}
setitemsToShow(DATA);
console.log(itemsToShow);
}
const MyFooter = (props) =>{
const [sometext, setsometext] = React.useState('');
return(
<View style={{borderWidth:1}}>
<TextInput value={sometext} onChangeText={(text) => setsometext(text)} placeholder="Enter data here"></TextInput>
</View>
)
}
React.useEffect(()=>{
PopulateTestData();
}, []);
return (
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "position" : "height"}>
<FlatList
data={itemsToShow}
ListFooterComponent = {MyFooter}
keyExtractor={(item) => item.key}
renderItem={(item) => <KeyboardAvoidingView style={{height:80, borderWidth:1}}><Text>{item.item.name}</Text></KeyboardAvoidingView>}
>
</FlatList>
</KeyboardAvoidingView>
)
}
basically it does not scroll the flatlist at all. it obviously works if i have just one or two items in the flatlist so it does not need to scroll to fit the keyboard.
here is a picture showing how the data entry field gets covered:
thanks,
Manish
let's try KeyboardAwareScrollView from react-native-keyboard-aware-scroll-view, some info here
I have this code, using React.useRef() but not working:
Main.js:
import * as React from "react"
export const Main: React.FunctionComponent<Props> = observer((props) => {
const ref = React.useRef()
React.useEffect(() => {
///Can not get ref from message
ref.gotoPosition(5)
}, [])
return (
<View style={styles.container}>
<Message
ref={ref}
getGotoIndex={getFunction}
onEndList={isShowQuickMove}
isSpeaker={state.isSpeaker}
questionsList={state.questionsList}
clickQuestion={clickQuestion}
isTyping={chatStore.loading}
data={state.data}/>
</View>
)
}
Message.js:
import * as React from "react"
// eslint-disable-next-line react/display-name
export const Message = React.forwardRef((props, ref) => ({
const { ... } = props
const gotoPosition = (index) => {
console.log('in here')
}
return (
<View>
....
</View>
)
}
)
I can not get ref from Message, even i used React.forwardRef. How to access gotoPosition function in Message by ref like ref.gotoPosition(5). Thanks
You are not passing the ref you get to the Flatlist all you need to do is pass it like so:
<FlatList
ref={ref} // create a referenece like so
extraData={[data, isSpeaker]}
onEndReached={handleEnd}
onEndReachedThreshold={0.4}
data={data}
keyExtractor={(item, index) => index.toString()}
renderItem={renderItems}
/>