How can I render 2 items in a FlatList in React-native and Redux - react-native

I need to render two items "clientcalendarscreenred" and "nutritiondata" in a FlatList
PS: I am getting the two data "clientcalendarscreenred" and "nutritiondata" from a reducer through mapStateToProps
<FlatList
data={this.props.clientcalendarscreenred }
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
ItemSeparatorComponent={this._renderSeparator}
refreshing={this.state.refreshing}
onRefresh={this._onRefresh}
/>
===========
GETTING The DATA
===============
const mapStateToProps = ({clientcalendarscreenred, maincalendarreducer}) => {
const { client_id, workout_date, display_date } = maincalendarreducer;
return {
clientcalendarscreenred: clientcalendarscreenred.data,
nutritiondata: clientcalendarscreenred.nutrition,
};
};

You can use section list for this scenario.
You can also render the list heterogeneous or homogeneous i.e if you wish to render your sections differently
<SectionList renderSectionHeader={({section}) => this._renderHeader(section.data)}
sections={[
{
key: 'RedData',
data: clientcalendarscreenred.data,
renderItem: ({item}) => this.renderRedData(item)
},
{
key: 'NutritionData',
data: clientcalendarscreenred.nutrition,
renderItem: ({item}) => this.renderNutrition(item, index, section)
},
]}
keyExtractor={(item, index) => index}
/>

Something like below should work for you to print items from data prop in FlatList.Similarly you can pass nutritiondata to FlatList and use it to display.
const FlatList=(props)=>{
const {data, ...rest}=props;
const toRender= data&&data.length>0?data.map(item=><div>{item.something}</div>:<div/>
return toRender
}

Related

Using FlatList when rendering anything more than a single "<Text>" causes error

I'm using FlatList as follows:
<FlatList
data={nodes}
renderItem={({ item }) => <Goal goal={item} />}
keyExtractor={item => item.id}
/>
I'm getting this error:
Error: Text strings must be rendered within a component.
when following component is passed to renderItem
const Goal = (props: { goal: GoalProps }) => {
const listTags = props.goal.tags.map(tag =>
<li key={tag.id}> {tag.name} </li>
);
return <>
<Text>goal title: {props.goal.title}</Text>
<ul>{listTags}</ul>
</>
}
When I simply return just one <Text> then it works without error.
This works:
const Goal = (props: { goal: GoalProps }) => {
const listTags = props.goal.tags.map(tag =>
<li key={tag.id}> {tag.name} </li>
);
return <Text>goal title: {props.goal.title}</Text>
}
How can I pass a more complex component to FlatList? Or maybe there is another list component I can use? I'm new to React and React Native.

Navigate from a function call React native

Hi guys I am having trouble finding a solution on my problem because I want to navigate to another page but here's the problem
function NewOrderPopUp({id, services, name, rating, accepted, destinationPlaceName, userPlaceName, driverName, driverContactNumber, driverRating, driverTrackingNumber})
{
async function toggleAcceptBooking()
{
await firestore()
.collection('userBookingRequest')
.doc(id)
.update({
accepted : !accepted,
driverName: 'Sample Driver',
driverContactNumber: '09123456789',
driverRating: '4.9',
driverPlateNumber: 'NFT-2020',
driverTrackingNumber: GenerateTrackingNumber(),
})
CheckIfBookingAccepted();
}
}
return (
<View>....</View>
);
export default NewOrderPopUp;
And I am calling the NewOrderPopUp Page in another file.
like this
import NewOrderPopUp from "../../components/NewOrderPopUp";
const HomeScreen = () => {
//... codes here
return (
<View>
<FlatList
horizontal
contentContainerStyle={{paddingHorizontal: 30}}
data={userBookingData}
keyExtractor={(item) => item.id}
renderItem={({item}) => <NewOrderPopUp {...item}/>} />
</View>
);
}
export default HomeScreen;
What I wanted is that if I click the toggleAcceptBooking it will nagivate to another page like
navigation.navigate('BookingPage');
Can someone enlighten me please . Thank you.
Do it by passing navigation down as a prop.
do the following steps.
handle navigation prop in HomeScreen
const HomeScreen = ({ navigation }) => {
}
pass navigation as a prop to NewOrderPopUp
<FlatList
...
renderItem={({item}) => <NewOrderPopUp navigation={navigation} {...item}/>} />
handle navigation prop in NewOrderPopUp and use it to navigate.
function NewOrderPopUp( {navigation, ...} ){
async function toggleAcceptBooking(){
await ...
navigation.navigate('BookingPage');
}
}

React Native: How to change inputfield to selected item

So what I'm trying to do is fetching data from an API (works well), that has this autocomplete function.
Link to example: https://autocomplete.aws.dk/
Link to the guide: https://autocomplete.aws.dk/guide2.html
What is hard for me, is that the guide is HTML, and this is suppose to work in React Native.
So far I made an input field, that can detect when writing minimum two letters will show a list of addresses.
What I want is when the address is clicked, it takes that value and places it in the input field.
Heres my code:
The API fetch:
import React from "react";
import url from "./url";
export default class DawaFetch extends React.Component {
static defaultProps = {
options: {},
minCharacters: 2,
};
state = {
value: "",
suggestions: [],
};
handleChange = ({ target: { value } }) => {
this.setState({ value });
if (this.props.minCharacters <= value.length) this._fetch(value);
};
_fetch = (value) => {
fetch(
url("https://dawa.aws.dk/adresser/autocomplete", {
q: value,
["per_side"]: 100,
...this.props.options,
}),
{
method: "GET",
headers: {
"Accept-Encoding": "gzip, deflate",
},
}
)
.then((response) => response.json())
.then((json) => this.setState({ suggestions: json }))
.catch((err) => console.error("parsing failed", err));
};
render = () =>
this.props.children({ ...this.state, handleChange: this.handleChange });
}
And here is my view:
<DawaFetch>
{({ value, suggestions, handleChange }) => {
console.log(suggestions);
return (
<View>
<CustomInputs
type="text"
value={value}
onChange={handleChange}
/>
{suggestions.map((suggestion) => (
<TouchableOpacity>
<NormalText key={suggestion.adresse.id}>{suggestion.tekst}</NormalText>
</TouchableOpacity>
))}
</View>
);
}}
</DawaFetch>
Tried different solutions by making it a FlatList with renderItem, and making an onPress function, but I could never make it work.
Hopefully someone can guide me in the right direction, I might been overthinking this.
React-Native TextInput onChange value is not available inside the target as it's available in HTML, Remove target from handleChange function like below, also it's not onChange it's onChangeText in TextInput.
handleChange = (value) => {
this.setState({ value });
if (this.props.minCharacters <= value.length) this._fetch(value);
};
You can achieve your desired functionality in a very simple manner.
Add this to your DawaFetch class.
OnItemSelection=(address)=>{
this.setState({value: address})
}
Add this to your render Function
render = () =>
this.props.children({ ...this.state, handleChange: this.handleChange, OnItemSelection: this.OnItemSelection });
}
Then make these changes in your DawaFetch component
<DawaFetch>
{({ value, suggestions, handleChange, OnItemSelection }) => {
console.log(suggestions);
return (
<View>
<CustomInputs
type="text"
value={value}
onChangeText={handleChange}
/>
{suggestions.map((suggestion) => (
<TouchableOpacity onPress={()=> OnItemSelection(suggestion.adresse)}>
<NormalText key={suggestion.adresse.id}>{suggestion.tekst}</NormalText>
</TouchableOpacity>
))}
</View>
);
}}
</DawaFetch>
Edit:Here is Snack having solution
https://snack.expo.io/#waheed25/bad-raisins

How to pass a list with objects from Server to FlatList?

I'm trying to create a FlatList that contains an array with objects that has an array from a server and I can't get it to work.
my error message:
TypeError: Cannot read proparty 'Data' of undefined
I can get it to work with my normal list that's not fetched from a Server. Heres the structure from my working list
[{"DATA":[{"filter_id":"44","filter_name":"filter 1"}, {"filter_id":"45","filter_name":"filter 2"},{"filter_id":"46","filter_name":"filter 3"},{"filter_id":"47","filter_name":"filter 4"},{"filter_id":"48","filter_name":"filter 5"}],"MESSAGE":"DATA FOUND","STATUS":200}]
My server list have the same structure but different values of filter_name and filter_id
here's my code:
constructor(props){
super(props);
this.state = {
data: [],
oldData: [{"DATA":[{"filter_id":"44","filter_name":"filter 1"},{"filter_id":"45","filter_name":"filter 2"},{"filter_id":"46","filter_name":"filter 3"},{"filter_id":"47","filter_name":"filter 4"},{"filter_id":"48","filter_name":"filter 5"}],"MESSAGE":"DATA FOUND","STATUS":200}],
page:1,
status: null,
isLoading: false,
}
}
getData = async () => {
const url = 'api/getFilter.php?page='+this.state.page+'&row_per_page=5';
fetch(url).then((response) => response.json())
.then((responseJson) => {
this.setState({
data:this.state.data.concat(responseJson),
isLoading:false
});
})
}
componentDidMount() {
this.getData();
}
renderRow = ({item}) => {
console.log('item', item);
return (
<View style={styles.item}>
<Text>{item.filter_name}</Text>
</View>
)
}
render() {
console.log('state', this.state.data[0]);
console.log('oldstate', this.state.oldData[0]) // this
return (
<View style={styles.container}>
<FlatList
data={this.state.data[0].DATA}
renderItem={this.renderRow}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
Expo: https://snack.expo.io/#thesvarta/tenacious-sandwich
The issue is that on the initial rendering of your component this.state.data is empty, because we have to wait until getData returns any data. That's why you cannot access this.state.data[0].DATA at the beginning.
The solution is to update your getData function a little bit.
getData = async () => {
const url = 'http://ollenorstrom.se/ollenorstrom.se/avoka/api/getFilter.php?page='+this.state.page+'&row_per_page=5';
fetch(url).then((response) => response.json())
.then((responseJson) => {
// here we save the data, we want to access later.
console.log('responseJson', responseJson[0].DATA);
this.setState({
data:this.state.data.concat(responseJson[0].DATA),
isLoading:false
});
})
}
Now your data is directly stored in this.state.data. We now can simplify your render() function:
<FlatList
data={this.state.data} // simplified, passing an empty array at the beginning is ok
renderItem={this.renderRow}
keyExtractor={(item, index) => index.toString()}
/>
Working example:
https://snack.expo.io/HJ--GFlnN

render same dataRow too much every time

Items re-render multiple times Whenever new Items are added to data prop array. So how can I avoid this useless re-rendering of items.
I am using
react-native 0.59.0
react 16.8.3
This is what I get when I log, Items in it's render method
renderItem 0
renderItem 1
renderItem 2
renderItem 3
// from here render again
renderItem 0
renderItem 1
renderItem 2
renderItem 3
renderItem 4
renderItem 5
renderItem 6
renderItem 7
//gettingData and displaying component
let offset = this.props.fetchedData.length;
//function to fetch data from server
getData() {
fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
limit: 20,
offset,
}),
})
.then(response => (response.json())
.then((responseJson) => {
if (responseJson.status) {
this.props.fetchedDataChange(responseJson.data);
}
})
.catch((error) => {
alert(error);
}));
}
//renderItem function for flatlist
renderItem({ item }) {
return (
<View>
<Text>
{item.name}
</Text>
</View>
);
}
render() {
return (
<View>
<FlatList
data={this.props.fetchedData}
renderItem={{ item } => this.renderItem({ item })}
keyExtractor={(item, index) => index.toString()}
extraData={this.props}
initialNumToRender={20}
maxToRenderPerBatch={20}
onEndReached={() => this.getData()}
onEndReachedThreshold={0.5}
/>
</View>
);
}
const mapStateToDispatchProps = dispatch => ({
fetchedDataChange: value => dispatch(fetchedDataChange(value)
});
const mapStateToProps = state => ({
fetchedData: state.fetchedDataReducer.fetchedData
});
export default connect(mapStateToProps, mapDispatchToProps)(gettingData);
//fetchedData reducer component
const INITIAL_STATE = {
fetchedData: [],
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCHED_DATA_CHANGED:
return {
...state,
fetchedData: [...state.fetchedData, ...action.payload]
};
default:
return state;
}
};
If your data prop is getting updated from state variable everytime state changes re-rendering will occur.
I believe you might be looking for React.PureComponent which implements a shallow comparison to decide if rerender is needed. You can also achieve this by implementing shouldComponentUpdate() on React.Component.
There is already an example in FlatList's docs that demonstrate how you can use PureComponent to render your list, just scroll down abit and look for the "more complex example" :)
If you prefer to use a Functional Component instead of a Class Component, checkout React.memo, which is similar to React.PureComponent but for Functional Components. Hope it helps.