react native usememo renderitem not working why? - react-native

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])

Related

How can I use useMemo React Hook in this example

What am I doing wrong here? I want to utilise useMemo so that my RenderItems component doesn't keep flickering when the state (Data2) changes. The Data2 array is in place of an item in my apps state. In practice, Data2 is data fetched from an api, and thus is subject to change and update.
I'm not looking for an alternative in this case, I'd just like to know how to use useMemo in this example - thanks!
import React, { useMemo } from 'react';
import {
View,
Text
} from 'react-native';
const CoursesWidget = (props) => {
const Data2 = [{ id: '11' }, { id: '22' }, { id: '33' }];
const coursesArray = Data2;
const RenderItems = useMemo(() => {
return (
coursesArray
.map((course) => {
return (
<View key={course.id}>
<Text>{course.id}</Text>
</View>
);
}),
[coursesArray]
);
});
//const Finito = useMemo(() => RenderItems(), [])
return (
<View>
<RenderItems />
</View>
);
};
export default CoursesWidget;
Snack: https://snack.expo.dev/rr8toaABT
I would suggest that you use a state and a FlatList instead of creating the elements using map. There is no need to use useMemo at all in this scenario and it will not fix your issue.
import React, { useState } from 'react';
import {
View,
Text,
FlatList,
SafeAreaView
} from 'react-native';
const CoursesWidget = (props) => {
const [data, setData] = useState([{ id: '11' }, { id: '22' }, { id: '33' }])
return (
<SafeAreaView style={{margin: 20}}>
<FlatList
data={data}
renderItem={({ item }) => {
return <View>
<Text>{item.id}</Text>
</View>
}}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
};
export default CoursesWidget;
Here is an updated version of your snack.
All that needs to be changed is moving the dependency array that you pass to useMemo to be the last parameter, and instead of calling it in the return like a jsx component, you put the value in brackets since it's not really a function anymore:
import React, { useMemo } from 'react';
import {
View,
Text
} from 'react-native';
const CoursesWidget = (props) => {
const Data2 = [{ id: '11' }, { id: '22' }, { id: '33' }];
const coursesArray = Data2;
const RenderItems = useMemo(() => {
return (
coursesArray
.map((course) => {
return (
<View key={course.id}>
<Text>{course.id}</Text>
</View>
);
})
);
}, [coursesArray]);
//const Finito = useMemo(() => RenderItems(), [])
return (
<View>
{ RenderItems }
</View>
);
};
export default CoursesWidget;
Here's the snack: https://snack.expo.dev/5GbI-k8Pb

React Native Datatable doesn't show after moving the fragment into Component

I've created the following component. React Native Paper Datatable rows aren't showing after moving it into component and linking it to json loop.
If we comment " and uncommented the commented block below, you will see the Datatable is showing. What am I doing wrong with my two components? I've done all console.log. All data are showing correctly but JSX elements aren't rendering inside Datatable.
I've created the following code on Snack: https://snack.expo.dev/#everestster/datatable-component
import React, {useEffect} from 'react';
import type {Node} from 'react';
import {View, ScrollView, Text, StyleSheet, Dimensions} from 'react-native';
import {DataTable as PaperDataTable} from 'react-native-paper';
const DataTable = props => {
const optionsPerPage = [2, 3, 4];
const [page, setPage] = React.useState(0);
const [itemsPerPage, setItemsPerPage] = React.useState(optionsPerPage[0]);
useEffect(() => {
setPage(0);
}, [itemsPerPage]);
const HeaderSection = (): Node => {
console.log(props.items);
if (props.items.length === 0) {
return;
}
return (
<PaperDataTable.Header>
{Object.keys(props.items[0]).forEach(function (key) {
if (key !== 'Id') {
<PaperDataTable.Title style={[styles.allCell]}>
{key}
</PaperDataTable.Title>;
}
})}
</PaperDataTable.Header>
);
};
const BodySection = (): Node => {
return (
<PaperDataTable.Row>
{Object.keys(props.items[0]).forEach(function (key) {
if (key !== 'Id') {
<PaperDataTable.Cell style={[styles.allCell]}>
{key}
</PaperDataTable.Cell>;
}
})}
</PaperDataTable.Row>
);
};
return (
<ScrollView style={styles.tableHolder}>
<ScrollView horizontal={true}>
<View style={{alignItems: 'center'}}>
<PaperDataTable style={styles.table}>
<HeaderSection />
<BodySection />
{/*<PaperDataTable.Header>
<PaperDataTable.Title>Name</PaperDataTable.Title>
<PaperDataTable.Title>Email</PaperDataTable.Title>
</PaperDataTable.Header>
<PaperDataTable.Row>
<PaperDataTable.Cell>John</PaperDataTable.Cell>
<PaperDataTable.Cell>john#gmail.com</PaperDataTable.Cell>
</PaperDataTable.Row>
<PaperDataTable.Row>
<PaperDataTable.Cell>Harry</PaperDataTable.Cell>
<PaperDataTable.Cell>harr#gmail.com</PaperDataTable.Cell>
</PaperDataTable.Row>
<PaperDataTable.Row>
<PaperDataTable.Cell>Jessica</PaperDataTable.Cell>
<PaperDataTable.Cell>jessica#gmail.com</PaperDataTable.Cell>
</PaperDataTable.Row>*/}
<PaperDataTable.Pagination
page={page}
numberOfPages={1}
onPageChange={p => setPage(p)}
optionsPerPage={optionsPerPage}
itemsPerPage={itemsPerPage}
setItemsPerPage={setItemsPerPage}
showFastPagination
optionsLabel={'Rows per page'}
/>
</PaperDataTable>
</View>
</ScrollView>
</ScrollView>
);
};
const styles = StyleSheet.create({
tableHolder: {},
table: {
paddingLeft: 50,
paddingRight: 50,
flex: 1,
},
allCell: {
marginRight: 20,
},
});
export {DataTable};
Any help will be appreciated.
The problem is in your structure. Your current BodySection is not returning the correct structure react-native-paper wants. I rewrote the BodySection function. Here is the snack: https://snack.expo.dev/#truetiem/datatable-component
const BodySection = (): Node => {
return props.items.map(function (item) {
return (
<PaperDataTable.Row>
{Object.keys(item).map((key) => key === 'Id' ? null : (
<PaperDataTable.Cell>
{item[key]}
</PaperDataTable.Cell>
))}
</PaperDataTable.Row>
);
});
};

How to convert Fetch to Axios and Class component to Functional component?

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).

How to pass selected data to another screen from Flatlist

I am still new in using React Native and Mobile Apps Development. I tried to copy the code from another tutorial and have little bit of understanding it.
I have Save.js, Feed.js and Details.js. I have successfully retrieved the data from Save.js to Feed.js using FlatList and RenderItem. Now, I want to pass only selected data from Feed.js to Details.js. But I am confused which way to use, whether useNavigation, getParam, withNavigation or anything else? And is there any difference between using Hooks and Class? Btw I'm using Hooks.
Save.js
import { View, TextInput, Image, Button, StyleSheet, TouchableOpacity, Text} from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
export default function Save(props, navigation) {
const [productName, setProductName] = useState("")
const [category, setCategory] = useState("")
return (
<View style={styles.inputView}>
<TextInput
placeholder="Product name..."
onChangeText={(productName) => setProductName(productName)}
/>
</View>
<View style={styles.inputView}>
<TextInput
placeholder="Category..."
onChangeText={(category) => setCategory(category)}
/>
</View>
Feed.js
function Feed(props, navigation) {
const { currentUser, posts } = props;
const { navigate } = useNavigation();
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
Details.js
export default function Details({ props, navigate, route }) {
const productName = props.navigation.route.params.productName;
const { navigate } = useNavigation();
const productName = useNavigationParam('productName');
return (
<View>
<Text>{productName}</Text>
<Text>{Category}</Text>
</View>
)
}
I am not sure which way to use in Details.js, so I just put all code I have used and tested.
the code bellow will help you and I think you have problem in destructing context this will help you. and remember navigation is an object inside props
Feed.js
function Feed(props) {
const { currentUser, posts, navigation } = props;
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
in Feed you dont need to use useNavigation() because props argument contain navigation.
Details.js
export default function Details(props) {
const {productName, category} = props.navigation.route.params;
return (
<TouchableOpacity onPress={()=>props.navigation.navigate("Save",{productName, category})}>
<Text>{productName}</Text>
<Text>{Category}</Text>
</TouchableOpacity>
)
}

How to get ref from custom component in react hook?

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}
/>