How make re-render Flatlist React Native? - javascript

I am doing a search filter for an TodoList.
In todoList I'm using a function to render items called renderItem, but I don't know how to re-render, when I write the searched item in the inputText
Can someone help me?
StaticContainer.js
import * as React from 'react';
import { FlatList, Text, View, StyleSheet, TouchableOpacity, TextInput } from 'react-native';
export default class StaticCounter extends React.Component {
constructor(props) {
super(props);
this.state = {
filter: '',
name: '',
}
this.renderItem = this.renderItem.bind(this)
this.setState = this.setState.bind(this)
}
setState(text){
this.state.filter = text
alert(JSON.stringify(this.state.filter))
this.renderItem
}
renderItem(obj){
if (this.state.filter != '') {
if (obj.item.desc.startsWith(this.state.filter)) {
console.log(typeof(obj.item.desc));
let key = obj.item.key
return(
<TouchableOpacity style={styles.container} onPress={()=> this.props.acessarDados(key)}>
<Text style={styles.cel}>{obj.item.desc}</Text>
</TouchableOpacity>
)
}else{
}
}else{
let key = obj.item.key
return(
<TouchableOpacity style={styles.container} onPress={()=> this.props.acessarDados(key)}>
<Text style={styles.cel}>{obj.item.desc}</Text>
</TouchableOpacity>
)
}
}
render() {
return (
<View>
<FlatList style={styles.lista} data={this.props.itens} renderItem={this.renderItem}/>
<TextInput style={styles.input} onChangeText={(text) =>{this.setState(text)}}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
justifyContent: 'center',
},
lista: {
marginTop: 90,
},
cel:{
paddingVertical: 20,
backgroundColor: '#E4EBEE',
fontSize: 18,
marginBottom: 2,
},
inputView:{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
input:{
backgroundColor: '#fff',
borderColor: '#ccc',
borderWidth: 3,
padding: 15,
margin:20,
color: '#3d3d3d'
}
});

you have to filter the data that you pass to the flatlist
state = {todoList: props.items }
filterTodo = (inputText) => {
const filteredList = this.state.todolist.filter(todo => todo.includes(inputText));
this.setState({todolist: filteredList })
}
then the data for your flatlist becomes this.state.todolist.
just to be clear the render Item is the component that get rendered from each data point in the data array, logic for the whole list should not be done there, if you don't want to show any data it should be a ternary in the data prop
data={someCondition? this.props.todolist: [] }
I think you have a typo in the flatlist data={this.props.itens}, this will work for a simple list if its a really big list you'll need a more optimized backend search.

Related

Cannot display Array in Flatlist format

Long story short, I'm trying to create a list of video-games. I have an autocomplete library that matches a game title with what I write in my input box.
I added a button that onPress triggers a function that pushes what I have in 'query'( which is what I am writing in my input box) into an array called myGamesArray
Then I'm trying to display myGamesArray in list form using Flatlist. But for some reason it is not showing up.
My code is a little messy because the solution I found to place a button next to my input box was by changing the flex direction to 'row'
I added a button right below the FlatList just to check if it would display underneath the inputbox (which it does) but for some reason the flatlist does not
Here's all my App.js
/*This is an example of AutoComplete Input/ AutoSuggestion Input*/
import React, { Component } from 'react';
//import react in our code.
import { StyleSheet, Text, TouchableOpacity, View, Image, Alert, FlatList } from 'react-native';
//import all the components we are going to use.
import Autocomplete from 'react-native-autocomplete-input';
import { Button, List, Container, ListItem, Header } from 'native-base';
//import Autocomplete component
//Demo base API to get the data for the Autocomplete suggestion
class App extends Component {
constructor(props) {
super(props);
//Initialization of state
//games will contain the array of suggestion
//query will have the input from the autocomplete input
this.state = {
myGamesArray: [],
games: [],
query: '',
};
}
componentDidMount() {
//First method to be called after components mount
//fetch the data from the server for the suggestion
fetch('https://api.rawg.io/api/games?page=1&platforms=18', {
"method": "GET",
"headers": {
"x-rapidapi-host": "rawg-video-games-database.p.rapidapi.com",
"x-rapidapi-key": "495a18eab9msh50938d62f12fc40p1a3b83jsnac8ffeb4469f"
}
})
.then(res => res.json())
.then(json => {
const { results: games } = json;
this.setState({ games });
//setting the data in the games state
});
}
findGame(query) {
//method called everytime when we change the value of the input
if (query === '') {
//if the query is null then return blank
return [];
}
const { games } = this.state;
//making a case insensitive regular expression to get similar value from the game json
const regex = new RegExp(`${query.trim()}`, 'i');
//return the filtered game array according the query from the input
return games.filter(game => game.name.search(regex) >= 0);
}
AddItemsToArray=()=>{
//Adding Items To Array.
this.state.myGamesArray.push( this.state.query.toString() )
// Showing the complete Array on Screen Using Alert (just to check if it's infact inside the array)
Alert.alert(this.state.myGamesArray.toString());
}
render() {
const { query } = this.state;
const games = this.findGame(query);
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
return (
<View>
<View style={styles.container}>
<Autocomplete
style={styles.autocompleteContainer1}
autoCapitalize="none"
autoCorrect={false}
//data to show in suggestion
data={games.length === 1 && comp(query, games[0].name) ? [] : games}
//default value if you want to set something in input
defaultValue={query}
/*onchange of the text changing the state of the query which will trigger
the findGame method to show the suggestions*/
onChangeText={text => this.setState({ query: text })}
placeholder="Selecione os jogos que voce quer receber!"
renderItem={({ item }) => (
//you can change the view you want to show in suggestion from here
//I GET ERROR WHEN TRYING TO ERASE (PS4) IN TEXT BOX ***NEED TO CHECK THIS
<View style={styles.iconContainer} >
<TouchableOpacity onPress={() => this.setState({ query: item.name})}
style={styles.itemContainer} >
<View>
<Image
style={styles.profilepic}
source={{uri: item.background_image}}
/>
</View>
<Text style={styles.itemText}>
{item.name} (PS4)
</Text>
</TouchableOpacity>
</View>
)}
/>
<Button
style={styles.addButton}
icon='plus'
title="Click Here To Add Value To Array"
onPress={() => this.AddItemsToArray()}
/>
<View style={styles.descriptionContainer}>
{games.length > 0 ? (
//Text inside input box
<Text style={styles.infoText}>{this.state.query}</Text>
) : (
<Text style={styles.infoText}>{this.state.query}</Text>
)
}
</View>
</View>
<View style={styles.container2}>
<Button
full></Button>
<FlatList
data={this.state.myGamesArray}
renderItem={({ item }) => (
<ListItem>{item}</ListItem>
)}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
flex: 1,
flexDirection:'row',
padding: 16,
marginTop: 40,
justifyContent: 'center',
},
container2: {
backgroundColor: '#fff',
flex: 1,
marginTop: 100,
alignItems: 'center',
justifyContent: 'center',
},
autocompleteContainer1: {
borderWidth: 1,
backgroundColor: "#fff",
borderColor: '#7843FF',
height: 50,
width:300
},
itemContainer: {
flex:1,
flexDirection: 'row',
borderWidth:0.5,
borderColor: '#7843FF',
paddingVertical: 5,
alignItems: 'center',
paddingRight: 60
},
addButton: {
width:50,
height: 50,
borderColor: '#7843FF',
borderWidth: 0.5,
backgroundColor: '#fff'
},
descriptionContainer: {
flex: 1,
justifyContent: 'center',
borderRadius: 10,
color: '#fff',
borderColor: '#7843FF'
},
itemText: {
fontSize: 15,
marginLeft: 10,
marginRight:30,
marginVertical:10,
color: '#000',
textAlign: 'left',
justifyContent: 'center'
},
infoText: {
textAlign: 'center',
color: '#000',
},
profilepic: {
flex: 3,
height: 60,
width: 60,
marginLeft:10,
borderRadius: 100,
},
});
export default App;
Issue
this.state.myGamesArray.push(this.state.query.toString()) is a state mutation. You need to actually do a state update and return a new array object reference.
Solution
Use a functional state update to access the previous state object, and shallow copy the old array into a new array reference and append the new query value to the end.
Also, logging or otherwise trying to reference the updated state right after enqueueing the update won't work like that, state is const and updated between render cycles so it'll enclose the current state.
AddItemsToArray = () => {
//Adding Items To Array.
this.setState(prevState => {
const { myGamesArray, query } = prevState;
return {
myGamesArray: [...myGamesArray, query.toString()],
};
},
// Use setState callback to alert with the updated state
() => {
// Showing the complete Array on Screen Using Alert (just to check if it's in fact inside the array)
Alert.alert(this.state.myGamesArray.toString());
},
);
}

Function inside Text Tag React Native

I am having an issue with the react-native Text tag. I could not figure out why this happening. Please, help out.
I need to use the function retrieveData inside the Text tag but that gives an error. What am I doing wrong? Thanks
I have set the data(mobileNumber as key) using AsyncStorage inside another file.
import React, { Component } from "react";
import {
View,
Text,
TextInput,
SafeAreaView,
TouchableOpacity,
AsyncStorage
} from "react-native";
export default class PhoneCode extends Component {
constructor(props) {
super(props);
this.state = {
};
}
onVerify = () => {};
onPress = () => {
alert("Sending...");
};
tappedVerify = () => {
console.log("clicked")
};
render() {
const { code, isCodeValid } = this.state;
const _retrieveData = async () => {
try {
const value = await AsyncStorage.getItem("mobileNumber");
if (value !== null) {
// We have data!!
return value;
}
} catch (error) {
// Error retrieving data
console.log("failed");
}
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.contentArea}>
<Text style={{}}> Enter... </Text>
<View style={styles.codeArea}>
<Text style={styles.codeInput}> {_retrieveData()} </Text> // this is the problem
<TextInput
placeholder="Enter here"
underlineColorAndroid="transparent"
style={styles.textInput}
maxLength={6}
autoGrow={true}
maxHeight={40}
keyboardType={"phone-pad"}
onChangeText={(text) =>
this.setState({
//....
})
}
/>
</View>
</View>
</SafeAreaView>
Here is the style object
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "flex-start",
alignItems: "center"
},
contentArea: {
justifyContent: "flex-start",
alignItems: "center",
width: '100%',
height: '100%',
paddingTop: 80,
paddingLeft: 20,
paddingRight: 20
},
textInput: {
textAlign: 'center',
height: 50,
width: '18%',
borderWidth: 2,
borderColor: 'gray',
borderRadius: 20,
backgroundColor: "#FFFFFF"
}
})
Here is the error message
ExceptionsManager.js:74 Invariant Violation: Objects are not valid as a React child (found: object with keys {_40, _65, _55, _72}). If you meant to render a collection of children, use an array instead.
Doing this solves my problem. It might help someone else.
<Text style={styles.codeInput}> {`${_retrieveData()}`} </Text>

How to force Flatlist to re-render after getting a single data?

I've faced an issue with flatlist when I get single data from the server and set these into state and passes into data props, I can't see any update in the render "I'm adding some Loading if I'd not received any data I show an let's say Indicator" so the indicator disappears and I see blank screen!!
FYI, When I enable Hot Reloading and just press Save in my IDE I can see the single Data in my Screen!
So how can I force it to appear the data!
Code
import React, { Component } from "react";
import firebase from "react-native-firebase";
import Icon from "react-native-vector-icons/Ionicons";
import _ from "lodash";
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
Image,
Dimensions
} from "react-native";
const { width } = Dimensions.get("screen");
class ListChats extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
noChat: true
};
}
_getUsers = () => {
let currentUser = firebase.auth().currentUser.uid;
let ref = firebase.database().ref(`Messages/${currentUser}`);
let usersKeys = [];
ref.once("value").then(snapshot => {
snapshot.forEach(childsnap => {
usersKeys.push(childsnap.key);
});
let usernames = [];
usersKeys.forEach(key => {
firebase
.database()
.ref("users")
.child(key)
.once("value")
.then(usersShot => {
let username = usersShot.val().username;
usernames.push({ username: username, key: key });
});
});
this.setState({ users: usernames,noChat: false });
});
};
componentDidMount() {
this._getUsers();
}
render() {
if (this.state.noChat) {
console.log("IF", this.state.users);
return (
<View style={styles.container}>
<Image
style={{
width,
height: width * 0.7,
resizeMode: "contain"
}}
source={require("../../assets/empty.gif")}
/>
<Text style={{ alignSelf: "center" }}>No Chats Found</Text>
</View>
);
} else {
console.log("Else", this.state.users);
return (
<View style={styles.container}>
<FlatList
key={Math.random() * 1000}
extraData={this.state} // I'm already added
data={this.state.users}
contentContainerStyle={{ flexGrow: 1 }}
renderItem={({ item }) => {
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("ChatDetails", {
Key: item.key,
userName: item.username
})
}
>
<View style={styles.parent}>
<Icon name="ios-contact" size={50} color="#4d8dd6" />
<View
style={{
flex: 1,
justifyContent: "flex-start",
alignItems: "flex-start",
marginHorizontal: 25
}}
>
<Text
style={{
color: "#000",
fontSize: 17
// marginHorizontal: 25
// alignSelf: "stretch"
}}
>
{item.username}
</Text>
{/* <Text
style={{
color: "#a1a1a1",
marginHorizontal: 35,
marginVertical: 5,
alignSelf: "stretch"
}}
numberOfLines={1}
lineBreakMode="tail"
>
{item.lastMssg.text}
</Text> */}
</View>
<Icon name="ios-chatboxes" size={25} color="#d6d6d6" />
</View>
</TouchableOpacity>
);
}}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center"
},
parent: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 25,
marginHorizontal: 15,
borderBottomWidth: 1,
borderBottomColor: "#eee"
}
});
export default ListChats;
You have used extra data props but the issue is flatlist does a shallow data comparison so when the length of users changes it won't affect the flatlist so replace it with this.state.users
extraData={this.state.users}
You can see in the documentation it says flatlist is implementation of PureCompoment and PureComponent does a shallow comparison thats the reason it is not re-rendering.
https://reactnative.dev/docs/flatlist#extradata
to force the flatlist re-render. just add a boolean lets say "forceUpdate" to your state and whenever an item is added to flatlist data toggle that boolean and pass that to the extra data of your FlatList instead of the state.

Can't find variable: StyleSheet

I'm studying React Native with this site https://www.tutorialspoint.com/react_native/react_native_animations.htm
However, there is a problem while i'm trying to open app on iPhone. According to error it cannot find variable, though it's imported.
import React, { Component } from 'react';
import { View, LayoutAnimation, TouchableOpacity, Text, StyleSheet} from 'react-native';
export default class Animations extends Component {
state = {
myStyle: {
height: 100,
backgroundColor: 'red'
}
};
expandElement = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
this.setState({
myStyle: {
height: 400,
backgroundColor: 'red'
}
})
};
collapseElement = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
this.setState({
myStyle: {
height: 100,
backgroundColor: 'red'
}
})
};
render() {
return (
<View>
<View>
<View style={this.state.myStyle}/>
</View>
<TouchableOpacity>
<Text style={styles.button} onPress = {this.expandElement}>
Expand
</Text>
</TouchableOpacity>
<TouchableOpacity>
<Text style={styles.button} onPress = {this.collapseElement}>
Collapse
</Text>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
button: {
borderWidth: 1,
borderColor: 'red',
color: 'red',
textAlign: 'center',
marginTop: 50,
padding: 10
}
});
Ah... I've found the problem. It was in other component which had styles but no elements to them and had no imported StylesSheet since I corrected it to new conditions but forgot about block with styles.

React Native - unexpected rendering of component

I am rendering a ListView in a React Native app that I'm building and am finding that a parent View container for the row is not expanding to fit all the content within it and I'm not sure why.
See in screenshot below how the second row within the row is rendered within the margin of the row view:
Component is as follows:
import React from 'react';
import { connect } from 'react-redux';
import {
View,
Text
} from 'react-native';
import { Icon } from 'native-base';
const ListViewRow = (props) => {
const { booking } = props;
const guest = props.guests[booking.guest_id];
return (
<View style={styles.rowStyle}>
<View style={styles.leftContentStyle}>
<View>
<Text>{guest.fullname}</Text>
</View>
<View>
<Text>{booking.start_date} - {booking.end_date}</Text>
</View>
</View>
<View style={styles.rightContentStyle}>
<Icon name='ios-arrow-forward' />
</View>
</View>
);
};
const styles = {
rowStyle: {
flex: 1,
borderBottomWidth: 1,
padding: 10,
backgroundColor: '#fff',
justifyContent: 'space-between',
flexDirection: 'row',
borderColor: '#ddd',
position: 'relative'
},
leftContentStyle: {
alignItems: 'stretch'
},
rightContentStyle: {
flexDirection: 'row',
alignItems: 'center'
}
};
const mapStateToProps = state => {
return { guests: state.guests };
};
export default connect(mapStateToProps)(ListViewRow);

Categories

Resources