Filtering simple Flatlist - reactjs

I want to filter this simple flatlist through a search bar. How do I code it so that whatever I write something on the input text it filters the flatlist? Could you help me completing it?
import React from 'react';
import { StyleSheet, Text, View, SafeAreaView, TextInput, TouchableOpacity, LayoutAnimation, Image, FlatList, ScrollView } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import {ListItem, SearchBar} from 'react-native-elements';
export default class HomeScreen extends React.Component{
render() {
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Home</Text>
</View>
<View style={styles.container1}>
<Icon name={"ios-search"} style={styles.icon}/>
<TextInput style={styles.inputBox}
underlineColorAndroid='rgba(0,0,0,0)'
placeholder="Procura aqui"
placeholderTextColor = "white"
selectionColor="black"
keyboardType="default"
/>
</View>
<View style={styles.flatlist}>
<FlatList
data = {[
{key:'Tiago'},
{key:'Ricardo'},
{key:'Beatriz'},
{key:'Miguel'},
{key:'Simão'},
{key:'David'}
]}
renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
/>
</View>
</View>
);
}
}

You should have a state value for searchtext and filter the array based on that. the component should be as below.
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: '',
};
}
render() {
//Data can be coming from props or any other source as well
const data = [
{ key: 'Tiago' },
{ key: 'Ricardo' },
{ key: 'Beatriz' },
{ key: 'Miguel' },
{ key: 'Simão' },
{ key: 'David' },
];
const filteredData = this.state.searchText
? data.filter(x =>
x.key.toLowerCase().includes(this.state.searchText.toLowerCase())
)
: data;
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Home</Text>
</View>
<View style={styles.container1}>
<Icon name={'ios-search'} style={styles.icon} />
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholderTextColor="white"
selectionColor="black"
keyboardType="default"
onChangeText={text => this.setState({ searchText: text })}
value={this.state.searchText}
/>
</View>
<View style={styles.flatlist}>
<FlatList
data={filteredData}
renderItem={({ item }) => (
<Text style={styles.item}>{item.key}</Text>
)}
/>
</View>
</View>
);
}
}

Please provide flatlist data from the state so you can control it while searching. Assuming if you want to bring those results at the top that matches your search text, you can do something like the below code. Firstly add onChangeText prop to the textinput and handle the input like this.
filterItems = (search_text) => {
var items = [...this.state.data];
var filtered = [];
if (search_text.length > 0) {
filtered = items.sort(
(a, b) => b.includes(search_text) - a.includes(search_text),
);
this.setState({data: filtered});
} else {
filtered = items.sort((a, b) => b - a);
this.setState({data: filtered});
}
};

Related

Getting the key of FLatList keyExtractor to make network call react native

I want to make network call to delete an item from an array. The end point is expecting me to pass the title as the ID of the item to be deleted in the array.
The array is of this form in the database:
payload: {
title: string,
description: string
}
Here is my implementations so far.
import React from "react";
import {
FlatList,
View,
Text,
StyleSheet,
ActivityIndicator,
} from "react-native";
import { Feather } from "#expo/vector-icons";
import { MaterialIcons } from "#expo/vector-icons";
import {
widthPercentageToDP as WP,
heightPercentageToDP as HP,
} from "react-native-responsive-screen";
import dimensions from "../../constants/dimensions";
import { fetchTodo, useDeleteTodo } from "../../server";
import showToast from "../../components/toast";
export default function TodoList({navigation}) {
const {data, isLoading } = fetchTodo()
console.log(data?.data)
const {mutateAsync} = useDeleteTodo()
const handleDelete = async (title:string) => {
try {
const response = await mutateAsync(title)
showToast(response.data.message);
// setClearTextInput("");
} catch (error) {
showToast(error);
}
};
return (
<View style={styles.container}>
<FlatList
contentContainerStyle={styles.contentContainer}
data={data?.data.payload}
keyExtractor={(ITodo) => ITodo.title}
renderItem={(renderTodo) => {
return (
<View style={styles.itemContainer}>
<Text style={styles.item}>{renderTodo.item.title}</Text>
<View style={styles.actionStyle}>
<Feather name="edit" size={WP(6)} color="blue"
onPress={()=>
navigation.navigate('EditTodoScreen')
}
/>
<MaterialIcons
name="delete"
size={WP(6)}
color="red"
onPress={() => {
handleDelete()
}}
/>
</View>
</View>
);
}}
ListEmptyComponent={() =>
isLoading ? (
<ActivityIndicator color="red" />
) : (
<Text style={{ marginTop: HP(8), fontSize: WP(7) }}>
List is empty
</Text>
)
}
/>
</View>
);
}
I have already made the title to be the key in the FlatList keyExtractor and I want to pass the title of the item clicked to the handleDelete(). It tried to pass "renderTodo" to it but i could not get the title from it. I am still new to react-native. Please how can i achieve this?
I would suggest you to do like this (This looks more clean and easy to understand.
keyExtractor={({ title }) => title}
renderItem={({ item }) => {
return (
<View style={styles.itemContainer}>
<Text style={styles.item}>{item.title}</Text>
<View style={styles.actionStyle}>
<Feather name="edit" size={WP(6)} color="blue"
onPress={()=>
navigation.navigate('EditTodoScreen')
}
/>
<MaterialIcons
name="delete"
size={WP(6)}
color="red"
onPress={() => {
handleDelete(item.title)
}}
/>
</View>
</View>
);
}}

How to store the parent's props in child component?

I am studying React Naive by making a ToDo list.
I want to change the ToDo app to a Comment List that has comment one to another.
First attempt worked correctly:
First attempt
But, all users changed after second attempt.
Second attempt
Now, parent passes props to child by using {this.state.pick} and {this.state.key} , but child would change if parent's props changed.
Is there any way to change the parent props without changing child props?
Comment.js:
export default class CommentIndex extends Component<{}> {
constructor(props) {
super(props);
this.state = {
head: [],
list: [],
pick: [],
};
}
_onPress = (text) => {
const list = [].concat(this.state.list);
list.push({
key: Date.now(),
text: text,
done: false,
});
this.setState({
list,
});
}
render() {
const {
head,
list,
pick,
} = this.state;
var data = [["User1", "User2", "User3"],];
return (
<View style={styles.container}>
<View style={styles.dropdownHeader}>
<View style={styles.dropdown}>
<DropdownMenu
style={{flex: 1}}
bgColor={'white'}
tintColor={'#666666'}
activityTintColor={'green'}
handler={(selection, row) => this.setState({head: data[selection][row]})}
data={data}
>
<View style={styles.userHeader}>
{ this.state.head === 'User1' && <User1 /> }
{ this.state.head === 'User2' && <User2 /> }
{ this.state.head === 'User3' && <User3 /> }
</View>
</DropdownMenu>
</View>
</View>
<Text>To Do</Text>
<View style={styles.main}>
<View style={styles.post}>
<View style={styles.dropdown}>
<View style={{height: 0}} />
<DropdownMenu
bgColor={'white'}
tintColor={'#666666'}
activityTintColor={'green'}
handler={(selection,row) => this.setState({pick: data[selection][row]})}
data={data}
>
<View style={styles.user}>
{ this.state.pick === 'User1' && <User1_photo /> }
{ this.state.pick === 'User2' && <User2_photo /> }
{ this.state.pick === 'User3' && <User3_photo /> }
</View>
</DropdownMenu>
</View>
<View style={styles.postinput}>
<CommentInput onPress={this._onPress} />
</View>
</View>
<View style={styles.CommentListContainer}>
<FlatList
style={styles.CommentList}
data={list}
renderItem={({ item }) => <CommentItem {...item} head={this.state.head} pick={this.state.pick}/>}
/>
</View>
</View>
</View>
);
}
}
CommentInput:
export default class CommentInput extends Component {
constructor(props) {
super(props);
this.ref = {};
}
_onPress = () => {
this.props.onPress(this.ref._lastNativeText);
this.ref.setNativeProps({ text: '' });
}
render() {
const {
onPress,
} = this.props;
return (
<View style={styles.container}>
<TextInput
style={styles.textInput}
ref={(ref) => { this.ref = ref; }}
/>
<TouchableOpacity
style={styles.button}
onPress={this._onPress}
>
<Text style={styles.buttonText}>追加</Text>
</TouchableOpacity>
</View>
);
}
}
You could implement the shouldComponentUpdate method on the child. This will block a re-render (with the new properties) when you want.

React Native data not updating when firebase updated

I am working on a project, I have done it 99%, but there's a problem.
Why the data doesn't change when I updated it?
In firebase the data has changed but my list still displays the previous data.
This is my code:
import React from 'react';
import { StyleSheet, Text, View, ListView,TouchableOpacity,Image,Alert,ActivityIndicator } from 'react-native';
import { Button, List, ListItem } from 'native-base'
import firebase from '../routes/set';
var data = []
export default class Menu extends React.Component {
constructor(props) {
super(props);
this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 })
this.state = {
listViewData: data,
loading:false
}
}
componentWillMount() {
var that = this
firebase.database().ref('/user').on('child_added', function (data) {
var newData = [...that.state.listViewData]
newData.push(data)
that.setState({ listViewData: newData })
})
}
async deleteRow(data,secId, rowId, rowMap) {
await firebase.database().ref('user/' + data.key).set(null)
rowMap[`${secId}${rowId}`].props.closeRow();
var newData = [...this.state.listViewData];
newData.splice(rowId, 1)
this.setState({ listViewData: newData });
}
render() {
return (
this.state.listViewData<1?<ActivityIndicator/>:
<View style={styles.container}>
<List
enableEmptySections
dataSource={this.ds.cloneWithRows(this.state.listViewData)}
renderRow={(data,secId,rowId,rowMap)=>
<ListItem>
<TouchableOpacity onLongPress={()=>
Alert.alert(null,'data',
[
{text:'delete',onPress:()=>this.deleteRow( data,secId, rowId, rowMap)},
{text:'edit',onPress:()=>this.props.navigation.navigate('Edit',{data:data,secId:secId,rowId:rowId,rowMap:rowMap})},
{text:'cancel',onPress:()=>null}
])
}>
<View style={{flexDirection:'row', marginLeft:20,marginTop:20}}>
<Image source={data.val().avatarSource} style={{width:50,height:50,borderRadius:30}}/>
<View style={{flexDirection:'column',marginLeft:20}}>
<Text> {data.val().name}</Text>
<Text> {data.val().age}</Text>
<Text> {data.val().gender}</Text>
<Text> {data.val().dob}</Text>
</View>
</View>
</TouchableOpacity>
</ListItem>
}
renderRightHiddenRow={( secId, rowId, rowMap,data) =>
<Button full danger onPress={() => this.deleteRow(secId, rowId, rowMap, data)}>
</Button>
}
/>
<TouchableOpacity onPress={()=>this.props.navigation.navigate('Daftar')}>
<Text>Insert</Text>
</TouchableOpacity>
</View>
);
}m
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});

Adding item click event in react native Grid View

Please find my code below and help me for adding item click listener for the items in the grid view. Please find the link which I followed library link.
And I need to display the name in an alert when the user clicks on each item in the gridlist. Styles are not included in the code
Thanks in Advance
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableWithoutFeedback
} from 'react-native';
import GridLayout from 'react-native-layout-grid';
class HomeScreen extends Component {
renderGridItem = (props) => (
<View style={styles.item} >
<View style={styles.flex} />
<Text style={styles.name} >
{props.name}
</Text>
</View>
);
render() {
const items = [];
for (let x = 1; x <= 30; x++) {
items.push({
name: `Grid ${x}`
});
}
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Grid Layout
</Text>
<View style={styles.flex}>
<GridLayout
items={items}
itemsPerRow={2}
renderItem={this.renderGridItem}>
</GridLayout>
</View>
</View>
);
}
}
export default HomeScreen;
Instead of using <View> in your renderGridItem, you could use one of the touchables component (react native doc).
For example with <TouchableOpacity >:
renderGridItem = (props) => (
<TouchableOpacity style={styles.item} onPress={() => this.showAlert(props.name)}>
<View style={styles.flex} />
<Text style={styles.name} >
{props.name}
</Text>
</TouchableOpacity>
);
showAlert = (name) => {
Alert.alert(
'Alert Title',
`The user name is: ${name}`,
[
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{ cancelable: false }
)
}
Why don't you wrap renderGridItem in a TouchableWithoutFeedback?
renderGridItem = (props) => (
<TouchableWithoutFeedback onPress={()=> Alert.alert(props.name)}>
<View style={styles.item} >
<View style={styles.flex} />
<Text style={styles.name} >
{props.name}
</Text>
</View>
<TouchableWithoutFeedback />
);
Also you will need to import Alert from react-native.

Add data in React Native and Save the State

I'm building a basic React Native application where the user enters his name in a TextInput and on button press his name along with his image is added to a ScrollView in another View. Images are stored and named in accord with the name of the person. Example - Name: 'ABC', the image fetched from assets will be 'ABC.jpg'.
I'm able to do this for one person, but every time I add a new entry the previous one gets overwritten. How can I retain the previous entry yet add another entry?
Home
import React, {Component} from 'react';
import { StyleSheet, Text, View, Image, ScrollView, Button, TouchableWithoutFeedback, TextInput} from 'react-native';
import { createStackNavigator } from 'react-navigation';
class HomeScreen extends React.Component {
render() {
const { navigation } = this.props;
const name0 = navigation.getParam('name0', 'NO-ID');
return (
<View style={styles.container}>
<ScrollView vertical={true} contentContainerStyle={{flexGrow: 1}}>
<Text style={styles.category}>Category 1</Text>
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
<TouchableWithoutFeedback onPress={() => {
this.props.navigation.navigate('Details', {
name: name0,
intro: 'lorem ipsum',
detail1: 'XYZ',
detail2: 'ABC',
});
}}>
<View style={styles.view}>
<Image source={require('./assets/rohit.jpg')} style={styles.image}></Image>
<Text style={styles.text}>{name0}</Text>
</View>
</TouchableWithoutFeedback>
</ScrollView>
</ScrollView>
</View>
)
}
}
Add Data
class AddData extends React.Component {
constructor(props) {
super(props);
this.state = { text: '' };
}
render() {
function onPressLearnMore() {
alert('Hi')
}
return (
<View style={{flex: 1, flexDirection: 'column', justifyContent:'center', alignItems: 'center'}}>
<TextInput
style={{height: 40,width:200}}
onChangeText={(text) => this.setState({input: text})}
/>
<Button
onPress={() => {
this.props.navigation.navigate('Home', {
name0: this.state.input
});
}}
title="Pass Data"
/>
</View>
);
}
}
Navigator
const RootStack = createStackNavigator(
{
Home: HomeScreen,
Details: DetailsScreen,
Data: AddData
},
{
initialRouteName: 'Data',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
Screen 1
Screen 2
Because you aren't saving your data from the user input anywhere, simply passing it back to home, it is getting overwritten and as your code sits, it is working! ;)
To simply put a bandaid on what you're doing, because this method of passing data is slightly taboo.. and you cannot achieve what you're asking without some sort of state persistence like AsyncStorage, or... to not have two screens. Because everytime React re-renders your screen any data that was in your state object disappears...
Inside your home component, on did mount you could store user input to state as objects inside an array. IN the below example I am using the user data you are sending back to home, however this would need to be data retrieved from some sort of persistence.
class HomeScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
usersArray: []
}
}
componentDidMount() {
let userInfo = this.props.navigation.getParam('name0', 'NO-ID');
userInfo !== undefined ? this.setState({usersArray:
[...this.state.usersArray, userInfo]}) : console.log('NO USER DATA to add')
}
render() {
const { navigation } = this.props;
const name0 = navigation.getParam('name0', 'NO-ID');
return (
<View style={styles.container}>
<ScrollView vertical={true} contentContainerStyle={{flexGrow: 1}}>
<Text style={styles.category}>Category 1</Text>
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
<TouchableWithoutFeedback onPress={() => {
this.props.navigation.navigate('Details', {
name: name0,
intro: 'lorem ipsum',
detail1: 'XYZ',
detail2: 'ABC',
});
}}>
<View style={styles.view}>
<Image source={require('./assets/rohit.jpg')} style={styles.image}></Image>
{/* <Text style={styles.text}>{name0}</Text> */}
{ this.state.usersArray.map((ele, index) => {
return <Text key={index} style={styles.text}>{ele.name}</Text>
})}
</View>
</TouchableWithoutFeedback>
</ScrollView>
</ScrollView>
</View>
)
}
}
Please keep in mind you may need to change your user data to be objects for this rest and spreading to work.

Resources