How do I manage multiple user inputs with only one onChange in React Native? - react-native

How do I handle multiple text inputs with only one onChange on React Native?
For example:
Name, age, nationality, eye color.
Additionally, how do I save all of the inputs with only one button? (I want to output a list with all of these inputs in the same "item")
Here's my code with what I did so far, I want to make a sort of movie diary where the user can register new movies they want to watch: (I'm a total beginner btw, so I'm not sure about how to do most things. I'm doing this project to learn)
import React, { useState } from 'react';
import { Button, StyleSheet, View, Text, TextInput, ScrollView } from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
const Registration = props => {
const [enteredMovieName, setMovieName] = useState("");
const [enteredSynopsis, setSynopsis] = useState("");
const [enteredComments, setComments] = useState("");
const [enteredSource, setSource] = useState("");
const movieData = {
Name: enteredMovieName,
Synopsis: enteredSynopsis,
Comments: enteredComments,
Source: enteredSource,
};
const movieDataHandler = () => {
console.log(movieData);
};
return (
<ScrollView>
<View>
<View>
<Text style={styles.bodyHighlight}>Add new movie</Text>
</View>
<ScrollView>
<View>
<Text style={styles.addMovie} >Movie name:</Text>
<TextInput
placeholder='Cool Movie Name'
style={styles.inputContainer}
onChangeText={enteredText => setMovieName(enteredText)}
value={enteredMovieName}
/>
<Text style={styles.addMovie} >Sinopsis:</Text>
<TextInput
placeholder='Amazing Synopsis'
style={styles.inputContainer}
onChangeText={enteredText => setSynopsis(enteredText)}
value={enteredSynopsis}
/>
<Text style={styles.addMovie} >Comments (optional):</Text>
<TextInput
placeholder='Awesome Thoughts'
style={styles.inputContainer}
onChangeText={enteredText => setComments(enteredText)}
value={enteredComments}
/>
<Text style={styles.addMovie} >Where to watch (optional):</Text>
<TextInput
placeholder='Super Useful Link'
style={styles.inputContainer}
onChangeText={enteredText => setSource(enteredText)}
value={enteredSource}
/>
</View>
<View>
<Button
style={styles.addMovie}
title='ADD'
color='#a30b00'
onPress={movieDataHandler}
/>
<Button
style={styles.addMovie}
title='SEE COMPLETE LIST'
color='#cd5c5c'
onPress={() => {
props.navigation.navigate('Items Screen');
}}
/>
</View>
</ScrollView>
</View >
</ScrollView>
)
}
const styles = StyleSheet.create({
bodyHighlight: {
padding: 10,
margin: 5,
fontWeight: 'bold',
fontSize: 25,
textAlign: 'center',
backgroundColor: '#C4BDBA'
},
inputContainer: {
borderColor: 'black',
borderWidth: 2,
width: 380,
justifyContent: 'center',
alignItems: 'center',
marginHorizontal: 10,
marginBottom: 5,
},
addMovie: {
padding: 10,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'stretch',
marginHorizontal: 10,
},
})
export default Registration;

You can manage all states in an object. For example:
import React from 'react';
import {
SafeAreaView,
StyleSheet,
TextInput,
Button,
Alert,
} from 'react-native';
const UselessTextInput = () => {
const [user, setUserData] = React.useState({ name: '', age: 0 });
return (
<SafeAreaView>
<TextInput
style={styles.input}
onChangeText={(text) => setUserData({...user, name: text })}
value={user.name}
/>
<TextInput
style={styles.input}
onChangeText={(age) => setUserData({...user, age: age })}
value={user.age}
/>
<Button onPress={() => Alert.alert(`User name ${user.name}, age ${user.age}`)} title="click me" />
</SafeAreaView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
marginTop: 42,
borderWidth: 1,
padding: 10,
},
});
export default UselessTextInput;

create a new function and make changes to the a copied object and push the new object to the state
const handleChange=(key,value)=>{
const myState = user
myState[key] = value
setUserData(myState)
}
then pass the function call to onChangeText prop
<TextInput
style={styles.input}
onChangeText={(text) => handleChange('name', text)}
value={user.name}
/>

Related

How to expand card onPress - React Native

I am trying to make a ticket app that allows for people to create tickets based on work that needs done. Right now, I need help with the expandable view for each ticket card. What I'm wanting is when a user presses on a specific card, it will expand the view and provide more details for only that card. What it is currently doing is expanding the view for every ticket card in the list. I'm new to React Native and trying my best, but nothing has worked so far.
Here is my parent which is called Home:
import React, {useState, useEffect} from 'react';
import {styles, Colors} from '../components/styles';
import { SafeAreaView } from 'react-native';
import Ticket from '../components/Ticket';
const data = [
{
name: 'Josh Groban',
subject: 'U-Joint',
desc: 'The bolt that is meant to hold the u-joint in place has the head broken off from it. See attached picture.',
assignee: 'John Doe',
assigneeConfirmedComplete: 'NA',
dateReported: 'Tue Mar 8, 2022',
vehicle: 'Truck 1',
media: '',
key: '1',
isShowing: false
},
// code removed for brevity
];
const Home = ({navigation}) => {
const [ticketList, setTicketList] = useState(data);
const getTickets = () => {
setTicketList(data);
}
useEffect(() => {
getTickets();
}, []);
return (
<SafeAreaView style={styles.HomeContainer}>
<Ticket
ticketList={ticketList}
setTicketList={setTicketList}
/>
</SafeAreaView>
)
};
export default Home;
And here is the main component that has all of the ticket card configurations:
import React, {useState, useEffect} from 'react';
import {Text, FlatList, View, SafeAreaView, Button, Image, TouchableOpacity } from 'react-native';
import {styles, Colors} from './styles';
import {Ionicons} from '#expo/vector-icons';
const Ticket = ({ticketList, setTicketList}) => {
const defaultImage = 'https://airbnb-clone-prexel-images.s3.amazonaws.com/genericAvatar.png';
const [isComplete, setIsComplete] = useState(false);
const [show, setShow] = useState(false);
const showContent = (data) => {
const isShowing = {...data, isShowing}
if (isShowing)
setShow(!show);
}
const completeTask = () => {
setIsComplete(!isComplete);
}
return (
<SafeAreaView style={{flex: 1}}>
<FlatList showsVerticalScrollIndicator={false}
data={ticketList}
renderItem={(data) => {
return (
<>
<TouchableOpacity key={data.item.key} onPress={() => showContent(data.item.isShowing = true)}>
<View style={styles.TicketCard}>
<Image
style={styles.TicketCardImage}
source={{uri: defaultImage}}
/>
<View style={styles.TicketCardInner}>
<Text style={styles.TicketCardName}>{data.item.vehicle}</Text>
<Text style={styles.TicketCardSubject}>
{data.item.subject}
</Text>
</View>
<TouchableOpacity>
<Ionicons
name='ellipsis-horizontal-circle'
color={Colors.brand}
size={50}
style={styles.TicketCardImage}
/>
</TouchableOpacity>
<TouchableOpacity onPress={completeTask}>
<Ionicons
name={isComplete ? 'checkbox-outline' : 'square-outline'}
color={Colors.brand}
size={50}
style={styles.TicketCardButton}
/>
</TouchableOpacity>
</View>
<View style={styles.TicketCardExpand}>
<Text>
{show &&
(<View style={{padding: 10}}>
<Text style={styles.TicketCardDesc}>
{data.item.desc}
</Text>
<Text style={{padding: 5}}>
Reported by: {data.item.name}
</Text>
<Text style={{padding: 5}}>
Reported: {data.item.dateReported}
</Text>
{isComplete && (
<Text style={{padding: 5}}>
Confirmed Completion: {data.item.assigneeConfirmedComplete}
</Text>
)}
</View>
)}
</Text>
</View>
</TouchableOpacity>
</>
)}}
/>
</SafeAreaView>
)
};
export default Ticket;
Lastly, here are the styles that i'm using:
import {StyleSheet } from "react-native";
import { backgroundColor } from "react-native/Libraries/Components/View/ReactNativeStyleAttributes";
// colors
export const Colors = {
bg: '#eee',
primary: '#fff',
secondary: '#e5e7eb',
tertiary: '#1f2937',
darkLight: '#9ca3f9',
brand: '#1d48f9',
green: '#10b981',
red: '#ff2222',
black: '#000',
dark: '#222',
darkFont: '#bbb',
gray: '#888'
}
export const styles = StyleSheet.create({
HomeContainer: {
flex: 1,
paddingBottom: 0,
backgroundColor: Colors.bg,
},
TicketCard : {
padding: 10,
justifyContent: 'space-between',
borderColor: Colors.red,
backgroundColor: Colors.primary,
marginTop: 15,
flexDirection: 'row',
},
TicketCardExpand: {
justifyContent: 'space-between',
backgroundColor: Colors.primary,
},
TicketCardImage: {
width: 60,
height: 60,
borderRadius: 30
},
TicketCardName:{
fontSize: 17,
fontWeight: 'bold'
},
TicketCardSubject: {
fontSize: 16,
paddingBottom: 5
},
TicketCardDesc: {
fontSize: 14,
flexWrap: 'wrap',
},
TicketCardInner: {
flexDirection: "column",
width: 100
},
TicketCardButton: {
height: 50,
}
});
Any help is greatly appreciated!
Create a Ticket component with its own useState.
const Ticket = (data) => {
const [isOpen, setIsOpen] = useState(false);
const handlePress = () => {
setIsOpen(!isOpen);
}
return (
<TouchableOpacity
onPress={handlePress}
>
// data.item if you use a list, otherwise just data
<YourBasicInformation data={data.item} />
{isOpen && <YourDetailedInformation data={data.item} />}
</TouchableOpacity>
)
}
Render one Ticket for every dataset you have.
<List
style={styles.list}
data={yourDataArray}
renderItem={Ticket}
/>
If you don't want to use a List, map will do the job.
{yourDataArray.map((data) => <Ticket data={data} />)}
instead of setting show to true or false you can set it to something unique to each card like
setShow(card.key or card.id or smth)
and then you can conditionally render details based on that like
{show == card.key && <CardDetails>}
or you can make an array to keep track of open cards
setShow([...show,card.id])
{show.includes(card.id) && <CardDetails>}
//to remove
setShow(show.filter((id)=>id!=card.id))

Fail to update parent component's state. Fail to reset form

function DateTimePick is a child function to render picked date and picked time in parent component Reservation. Currently there are two issues with the codes: 1. the form in Reservation does not reset after submit data. 2. child DateTimePick fails to update the state for 'date' and 'time' in parent Reservation. Please have a look and lemme know how to fix these. Thanks.
child function DateTimePick is below:
import React, {useState} from 'react';
import {View, Button, Platform, Text, TextInput, StyleSheet} from 'react-native';
import DateTimePicker from '#react-native-community/datetimepicker';
import { Icon } from 'react-native-elements';
import Moment from "moment";
export const DateTimePick = () => {
const [date, setDate] = useState(new Date());
const [mode, setMode] = useState('date');
const [show, setShow] = useState(false);
const onChange = (event, selectedDate) => {
const currentDate = selectedDate || date;
setShow(Platform.OS === 'ios');
setDate(currentDate);
};
const showMode = (currentMode) => {
setShow(true);
setMode(currentMode);
};
const showDatepicker = () => {
showMode('date');
};
const showTimepicker = () => {
showMode('time');
};
return (
<View>
<View style={styles.formRow}>
<Text style={styles.formLabel}> Date</Text>
<Text onPress={showDatepicker} style={styles.formItem} value_date={date.toDateString()} onChange = {(value_date) => this.props.setState({date: value_date})}><Icon type='font-awesome-5' name='calendar' color='#512DA8' />{' ' + Moment(date).format('DD-MMM-YYYY') }</Text>
</View>
<View style={styles.formRow}>
<Text style={styles.formLabel}> Time</Text>
<Text onPress={showTimepicker} style={styles.formItem} value_time={date.toTimeString()} onChange = {(value_time) => this.props.setState({time: value_time})}><Icon type='font-awesome-5' name='clock' color='#512DA8' /> {' ' + Moment(date).format('h:mm A') }</Text>
</View>
{show && (
<DateTimePicker
testID="dateTimePicker"
value={date}
mode={mode}
is24Hour={true}
display="default"
onChange={onChange}
/>
)}
</View>
);
};
//export default DateTimePick;
const styles = StyleSheet.create({
formRow: {
alignItems: 'center',
justifyContent: 'center',
flex: 1,
flexDirection: 'row',
margin: 20
},
formLabel: {
fontSize: 18,
flex: 1
},
formItem: {
flex: 1
},
modal: {
justifyContent: 'center',
margin: 20
},
modalTitle: {
fontSize: 24,
fontWeight: 'bold',
backgroundColor: '#512DA8',
textAlign: 'center',
color: 'white',
marginBottom: 20
},
modalText: {
fontSize: 18,
margin: 10
},
})
parent Reservation which produces a reservation form and a reservation input confirmed modal
import React, {Component} from 'react';
import { Text, View, ScrollView, StyleSheet, Switch, Button, TextInput, Modal} from 'react-native';
import {DateTimePick} from './DateTimePickComponent';
class Reservation extends Component {
constructor(props) {
super(props);
this.state = {
guests: 1,
smoking: false,
notes:'',
date: new Date().toDateString(),
time: new Date().toTimeString(),
showModal: false
}
}
toggleModal() {
this.setState({showModal: !this.state.showModal})
}
handleReservation() {
console.log(JSON.stringify(this.state)); //log current state
//this.setState({ // reset the form
//})
this.toggleModal();
}
resetForm() {
this.setState({
guests: 1,
smoking: false,
notes:'',
date: new Date().toDateString(),
time: new Date().toTimeString(),
showModal: false
});
}
render() {
//, transform: [{ scaleX: 3 }, { scaleY: 1.5 }] => for switch
return(
<ScrollView>
<View style={styles.formRow}>
<Text style={styles.formLabel}> Guests </Text>
<TextInput style={styles.formItem} keyboardType="numeric" placeholder="Number"
value_guests={this.state.guests}
onChangeText = {(value_guests) => this.setState({guests: value_guests})} >
</TextInput>
</View>
<View style={styles.formRow}>
<Text style={styles.formLabel}> Notes </Text>
<TextInput style={styles.formItem} keyboardType="default" placeholder="Allergy,..etc"
value_notes={this.state.notes}
onChangeText = {(value_notes) => this.setState({notes: value_notes})} >
</TextInput>
</View>
<View style={styles.formRow}>
<Text style={styles.formLabel}> Non-Smoking </Text>
<Switch style={{ flex: 1, backgroundColor: "orange", paddingLeft:0, marginLeft:0 }} trackColor={{true: 'red', false: 'grey'}}
value={this.state.smoking}
onValueChange = {(value) => this.setState({smoking: value})} />
<Text style={styles.formLabel}> Smoking </Text>
</View>
<DateTimePick />
<View style={styles.formRow}>
<Button
onPress={() => this.handleReservation()}
title="Reserve"
color="#512DA8"
accessibilityLabel="Learn more about this purple button"
/>
</View>
<Modal animationType={'slide'} transparent={false} visible={this.state.showModal}
onDismiss={() => {this.toggleModal()}}
onRequestClose={() => {this.toggleModal()}}>
<View style={styles.modal}>
<Text style={styles.modalTitle}>Your Reservation</Text>
<Text style={styles.modalText}>Number of Guests: {this.state.guests}</Text>
<Text style={styles.modalText}>Notes: {this.state.notes}</Text>
<Text style = {styles.modalText}>Smoking?: {this.state.smoking ? 'Yes' : 'No'}</Text>
<Text style = {styles.modalText}>Date and Time: {this.state.date} {this.state.time}</Text>
<Button
onPress = {() =>{this.toggleModal(); this.resetForm();}}
color="#512DA8"
title="Close"
/>
</View>
</Modal>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
formRow: {
alignItems: 'center',
justifyContent: 'center',
flex: 1,
flexDirection: 'row',
margin: 20
},
formLabel: {
fontSize: 18,
flex: 1
},
formItem: {
flex: 1
},
modal: {
justifyContent: 'center',
margin: 20
},
modalTitle: {
fontSize: 24,
fontWeight: 'bold',
backgroundColor: '#512DA8',
textAlign: 'center',
color: 'white',
marginBottom: 20
},
modalText: {
fontSize: 18,
margin: 10
},
})
export default Reservation;
you could fix it by transfering state consts date, show, mode to parent Reservation
then pass state change functions to the DateTimePick component in props like below
<DateTimePick onDateChange={(date) => this.setState({date: date}) } />
and in DateTimePick component:
const onChange = (event, selectedDate) => {
const currentDate = selectedDate || date;
setShow(Platform.OS === 'ios');
props.onDateChange(currentDate);
};
also dont forget to pass props in the component like
export const DateTimePick = (props) => {
Solved it. For using child data to update parents' state need to pass a handler for setting the state of parent to child, refer How to update parent's state in React?
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
}
handler() {
this.setState({
someVar: 'some value'
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {this.props.handler}/ >
}
}
In implementation, a handler is inserted into parent class Reservation as below
...
class Reservation extends Component {
constructor(props) {
...
this.handler = this.handler.bind(this)
}
handler(selectedDate) {
this.setState({
date: Moment(selectedDate).format('DD-MMM-YYYY'),
time: Moment(selectedDate).format('h:mm A'),
})
}
...
In child DateTimePick, embed the handler in onChange to pass child update from onChange to state update in parent Reservation like this
...
export const DateTimePick = (props) => {
const handler = props.handler;
...
const onChange = (event, selectedDate) => {
...
handler(currentDate);
};
...
...

Select and unselect component when one is selected

I've to display three components (cards) from which the user can select one. I've placed those three components inside a ScrollView as:
...
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
<LocationAndPriceCard
price={'100'}
title={'Choice 3'} />
<LocationAndPriceCard
price={'200'}
title={'Choice 2'} />
<LocationAndPriceCard
price={'300'}
title={'Choice 1'}} />
</ScrollView>
...
Here is how LocationAndPriceCard is coded:
...
function LocationAndPriceCard({ price, title }) {
const [selectedLocation, setSelectedLocation] = useState("red")
const styles = getStyles(selectedLocation);
const selected = () => {
if (selectedLocation == "red") {
setSelectedLocation("green")
} else {
setSelectedLocation("red")
}
}
return (
<TouchableOpacity style={styles.frame} onPress={selected}>
<Text style={styles.priceTxt}>RM {price}</Text>
<Text style={styles.title} numberOfLines={2}>{title}</Text>
</TouchableOpacity>
);
}
const getStyles = (borderC) => StyleSheet.create({
frame: {
borderWidth: 1,
borderColor: borderC,
width: 180,
backgroundColor: '#fff',
borderRadius: 8,
margin: 5,
padding: 10,
},
...
In the code above when the cad has selected it successfully change the border color to green but I can change the color of all the components. I want to make it like if one is selected all others should go back to red color.
Create two new props for LocationAndPriceCard, value and onPress.
Use value to determine which card is selected and based on that change the border color.
Use the onPress function to set the state which will have the title of the selected card, which we will be used to determine which card is selected.
Full Working Example: Expo Snack
import React, { useState } from 'react';
import {
Text,
View,
StyleSheet,
ScrollView,
TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App() {
const [selected, setSelected] = useState(null);
const handleSelected = (value) => {
setSelected(value);
};
return (
<View style={styles.container}>
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
<LocationAndPriceCard
price={'100'}
title={'Choice 3'}
onPress={handleSelected}
value={selected}
/>
<LocationAndPriceCard
price={'200'}
title={'Choice 2'}
onPress={handleSelected}
value={selected}
/>
<LocationAndPriceCard
price={'300'}
title={'Choice 1'}
onPress={handleSelected}
value={selected}
/>
</ScrollView>
</View>
);
}
function LocationAndPriceCard({ price, title, onPress, value }) {
return (
<TouchableOpacity
style={[styles.frame, { borderColor: value === title?"green":"red" }]}
onPress={()=>onPress(title)}>
<Text style={styles.priceTxt}>RM {price}</Text>
<Text style={styles.title} numberOfLines={2}>
{title}
</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
frame: {
borderWidth: 1,
width: 50,
height: 50,
backgroundColor: '#fff',
borderRadius: 8,
margin: 5,
padding: 10,
},
});

How can use useState() with Flatlist data?

I've had a problem when i used useState(). i have to filter by searched words on my data and list.
i need to define my data list with State (i'd list with searched words) but when i use State, i've taken 'Invalid Hook' error.
let [list, setList] = useState(data);
//called
data={list}
I don't find where i use that , I couldn't fix for 3 days, i can't reach next step :( I hope i'll fix with expert helps...
import React, {Component, useState} from 'react'
import {
Text,
StyleSheet,
View,
FlatList,
SafeAreaView,
ScrollView,
Image,
TextInput,
} from 'react-native'
import data from '../../data'
export default class Flatlistexample extends Component {
render () {
//defined below
let [list, setList] = useState(data);
seachFilter=(text)=>{
const newData = data.filter(item=>{
const listitem= `${item.name.toLowerCase()} ${item.company.toLowerCase()}`;
return listitem.indexOf(text.toLowerCase())
})
};
return (
<SafeAreaView
style={{
flex: 1,
}}>
<FlatList
//called
data={list}
renderItem={({item, index})=>{
return (
<ScrollView>
<SafeAreaView
style={[
styles.container,
{backgroundColor: index % 2 === 0 ? '#fafafa' : '#bbb'},
]}>
<Image style={styles.profile} source={{uri: item.picture}} />
<View style={styles.rightside}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.company}>{item.company}</Text>
</View>
</SafeAreaView>
</ScrollView>
)
}}
keyExtractor={item => item._id}
ListHeaderComponent={() => {
const [search, setSearch] = useState('');
return (
<View style={styles.seachContainer}>
<TextInput
style={styles.textInput}
placeholder={'Search...'}
value={search}
onChangeText={text=>{
setSearch(text)
}}
></TextInput>
</View>
)
}}
/>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
borderBottomWidth: 1,
borderColor: 'gray',
},
profile: {
width: 50,
height: 50,
borderRadius: 25,
marginLeft: 10,
},
rightside: {
marginLeft: 20,
justifyContent: 'space-between',
marginVertical: 5,
},
name: {
fontSize: 22,
marginBottom: 10,
},
searchContainer: {
padding: 10,
borderWidth: 2,
borderColor: 'gray',
},
textInput: {
fontSize: 16,
backgroundColor: '#f9f9f9',
padding: 10,
},
})
Thank you
React hooks can be used with functional component only, here you are using class component
You need to understand the difference between functional component and class component first.
Here you are using class component so your state should be manageed in the following way
export default class Flatlistexample extends Component {
constructor(props)
{
this.state={list:[]}
}
}
and to update list
this.setState({list: <array of data>})
If you want to use hooks, your component needs to be changed something like the following:
const Flatlistexample = () => {
//defined below
let [list, setList] = useState(data);
seachFilter = (text) => {
const newData = data.filter(item => {
const listitem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`;
return listitem.indexOf(text.toLowerCase())
})
};
return (
<SafeAreaView
style={{
flex: 1,
}}>
<FlatList data={list} renderItem={Your flatlist Item}/>
</SafeAreaView>
)
}
export default Flatlistexample
Here you go, I've added lots of comments. I hope you find this instructive. Let me know if you have questions!
import React, { useMemo, useState } from 'react'
import {
Text,
StyleSheet,
View,
FlatList,
SafeAreaView,
ScrollView,
Image,
TextInput,
} from 'react-native'
import data from '../../data'
// changed this to a functional component so you can use hooks. You can't use hooks in class components.
const Flatlistexample = () => {
// you don't actually need to `useState` for your list, since you're always just filtering `data`
// you would need to use `useState` if you were receiving data from an API request, but here it's static
const [search, setSearch] = useState('') // this should live in the main component so you can filter the list
const parsedSearch = search.toLowerCase() // do this once outside the filter, otherwise you're converting it for each item in the data array
const filteredList = useMemo(
() =>
data.filter(item => {
const itemText = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return itemText.indexOf(parsedSearch) > -1 // returns `true` if search is found in string
}),
[parsedSearch], // this will only run if parsedSearch changes
)
return (
<SafeAreaView style={{ flex: 1 }}>
<FlatList
//called
data={filteredList} // use the filtered list here
renderItem={({ item, index }) => {
return (
<ScrollView>
<SafeAreaView
style={[
styles.container,
{ backgroundColor: index % 2 === 0 ? '#fafafa' : '#bbb' },
]}
>
<Image style={styles.profile} source={{ uri: item.picture }} />
<View style={styles.rightside}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.company}>{item.company}</Text>
</View>
</SafeAreaView>
</ScrollView>
)
}}
keyExtractor={item => item._id}
ListHeaderComponent={() => {
return (
<View style={styles.seachContainer}>
<TextInput
style={styles.textInput}
placeholder={'Search...'}
value={search}
onChangeText={text => {
setSearch(text)
}}
/>
</View>
)
}}
/>
</SafeAreaView>
)
}
export default Flatlistexample

Change a button color when textinput is null, by using onPress

I just started to learn React Native technology, so i try to change some lines in tutorial code. This is a form, which add new title, but I want to change color of button if value === "". I try to find, but mostly examples use classes, in this project I want to use functions
import React, { useState } from 'react'
import { View, StyleSheet, TextInput, Button, Alert } from 'react-native'
export const AddTodo = ({ onSubmit }) => {
const [value, setValue] = useState('')
const pressHandler = () => {
if (value.trim()) {
onSubmit(value)
setValue('')
} else {
}
}
return (
<View style={styles.block}>
<TextInput
style={styles.input}
onChangeText={setValue}
value={value}
disabled
placeholder='Введите название дела...'
autoCorrect={false}
autoCapitalize='none'
/>
<Button title='Добавить' onPress={pressHandler} />
</View>
)
}
const styles = StyleSheet.create({
block: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 15
},
input: {
width: '70%',
padding: 10,
borderStyle: 'solid',
borderBottomWidth: 2,
borderBottomColor: '#3949ab'
},
button: {
color: 'red'
}
})
You use a controlled input field. You store the value in your state and you change it onChange with the input field. Next step is to set a style for your button depending on your current state.
<TextInput
style={[styles.input, value === '' ? styles.red : null]}
onChangeText={setValue}
value={value}
disabled
placeholder='Введите название дела...'
autoCorrect={false}
autoCapitalize='none'
/>
In this case you need to add a style called "red" which changes the button color.
red: {
backgroundColor: 'red'
}
Something like this.
Because your state is updated every time you change the input value, it gets updated onChange. If you want to set it on submit you need to add a isSubmitted boolean (default false ) to your state and set it to true in your pressHandler.
You need to destructure isSubmitted in this example:
style={[styles.input, value === '' && isSubmitted ? styles.red : null]}
You can apply a simple logic as below
<Button
title="Добавить"
onPress={pressHandler}
color={value === null ? 'red' : 'green'}
/>
EDIT
Check below sample which I created according to your requirement
import React, { useState } from 'react';
import { View, StyleSheet, TextInput, Button } from 'react-native';
export default App = () => {
const [value, setValue] = useState('');
const [error, handleError] = useState(false);
const pressHandler = () => {
if (value.trim()) {
setValue('');
} else {
handleError(true);
}
};
const onHandleChange = (text) => {
setValue(text)
handleError(false)
}
return (
<View style={styles.block}>
<TextInput
style={styles.input}
onChangeText={onHandleChange}
value={value}
placeholder="Введите название дела..."
autoCorrect={false}
autoCapitalize="none"
/>
<Button
title="Добавить"
onPress={pressHandler}
color={error ? 'red' : 'green'}
/>
</View>
);
};
const styles = StyleSheet.create({
block: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 15,
},
input: {
width: '70%',
padding: 10,
borderStyle: 'solid',
borderBottomWidth: 2,
borderBottomColor: '#3949ab',
}
});
Hope this helps you. Feel free for doubts.