Related
What I'm trying to do:
Make a search bar and show the results in a flat list under the bar.
What I have:
I have a SearchBar and a FlatList, the FlatList needs to but in absolute position so it covers the content on the bottom of the search bar
The Problem:
The FlatList is covering the search bar when it's active and I can't scroll the list or select an item. What I noticed is that if i try to select an item or scroll the list when clicking where the SearchBar should be appearing, I can select and scroll the list.
What I need:
The FlatList to show under the SearchBar and be able to scroll it.
I could use top: 50 to show the FlatList under the SearchBar but it doesn'r seems good
Observations: I'm not that good at styles
import React, { Component } from 'react'
import { Text, View, StyleSheet, TouchableHighlight, FlatList } from 'react-native'
import {
Slider,
SearchBar,
ListItem,
} from 'react-native-elements'
export default class SearchForm extends Component {
state = {
pages: 1,
displayList: false,
itemsPerPage: 5,
}
componentDidMount = async () => {
const {
data = [],
itemsPerPage = 5,
} = this.props
await fetch('https://servicodados.ibge.gov.br/api/v1/localidades/estados', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then(res => res.json())
.then(data => this.setState({
data: data,
displayData: data.slice(0, itemsPerPage)
}))
console.log(this.state.data.length)
}
updateSearch = search => {
const { data, itemsPerPage } = this.state
let s = search ? search.toLowerCase() : ''
this.setState({
displayData: data.filter(res => res.nome.toLowerCase().includes(s)).slice(0, itemsPerPage),
displayList: true,
search: search,
pages: 1,
})
if(this.flatListRef){
this.flatListRef.scrollToOffset({ animated: true, offset: 0 })
}
}
loadMore = () => {
const { pages, displayData, data, search, itemsPerPage } = this.state
const start = pages * itemsPerPage
let end = (pages + 1) * itemsPerPage
let s = search ? search.toLowerCase() : ''
const newData = data.filter(res => res.nome.toLowerCase().includes(s)).slice(start, end)
this.setState({
displayData: [...displayData, ...newData],
pages: pages + 1,
})
console.log(this.state.displayData.length)
}
selectItem = (value) => {
this.setState({
search: value,
displayList: false,
})
}
renderItem = ({ item, index }) => {
return (
<ListItem
style={styles.flatListItem}
containerStyle={styles.flatListItemCointainer}
key={index}
title={item.nome}
onPress={() => this.selectItem(item.nome)}
/>
);
}
render() {
const {
search,
displayData = [],
displayList,
} = this.state
return (
<View style={styles.container}>
<SearchBar
ref={search => { this.search = search }}
placeholder="Type Here..."
leftIcon={false}
noIcon
onChangeText={this.updateSearch}
value={search}
/>
{displayList && <FlatList
style={styles.flatList}
ref={ref => this.flatListRef = ref}
data={displayData}
keyExtractor={(item, index) => index.toString()}
renderItem={this.renderItem}
onEndReached={this.loadMore}
onEndReachedThreshold={0.5}
/>}
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ text })}
value={this.state.text}
/>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ text })}
value={this.state.text}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
alignSelf: 'stretch',
backgroundColor: '#fff',
},
flatList: {
height: 200,
width: '100%',
position: 'absolute',
},
flatListItemCointainer: {
backgroundColor: 'rgba(0,0,0,1)'
}
})
Edit: I just change the code a little bit to show what I'm trying to do. Under the SearchBar will have other components (e.g. TextInput) and when the list is active, the list should go on top of that components.
With Shashin Bhayani answer, it's not going on top of things under it, only pushing it down.
This issue in android, to solve it :
import { FlatList } from 'react-native-gesture-handler';
Adding bottom: 0 solved my issue I am using zero because I want the Faltlist to end at the very bottom of the screen it can be any number.
Make sure there is no flex: 1 in flatlist style, contentContainerStyle or on the parent flatlist.
Try this and let me know this solved your issue or not.
I am using FlatList to write an infinite scroll, but it keeps sending request to my server forever. please see the code blow. I don't find any article clarify when the next page will load, what exactly does the onEndReached will be triggered.
import React, { Component } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator, AsyncStorage } from 'react-native';
import { connect } from 'react-redux';
import { loadOrders } from '../redux/modules/Order';
import OrderListItem from './OrderListItem';
import { forOwn, isEmpty, reduce } from 'lodash';
class OrderList extends Component {
constructor(props) {
super(props);
this.state = {
page: 1,
error: null,
};
}
componentDidMount() {
this.loadOrders();
}
loadOrders = () => {
const { page } = this.state;
AsyncStorage.getItem("userToken")
.then((value) => {
return `Bearer ${value}`;
})
.then((userToken) => {
return this.props.loadOrders(page, { Authorization: userToken });
})
.then((response) => {
this.setState({
error: response.error || null,
});
})
.catch(error => {
this.setState({ error});
})
;
}
handleLoadMore = () => {
this.loadOrders();
};
onPressItem = (id: string) => {
};
keyExtractor = (item, index) => `order-item-${item.id}`;
renderItem = ({item}) => (
<OrderListItem
order={item}
onPressItem={this.onPressItem}
/>
);
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderFooter = () => {
if (!this.props.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
const { orders} = this.props;
if (orders.length> 0) {
return (
<View containerStyle={styles.container} >
<FlatList
data={orders}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
ListFooterComponent={this.renderFooter}
ItemSeparatorComponent={this.renderSeparator}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
/>
</View>
);
}
return <View>
<Text>empty</Text>
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
borderTopWidth: 0,
borderBottomWidth: 0
},
item: {
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#ccc'
}
});
const mapStateToProps = state => {
let order = state.get('order').toJS();
return {
orders: isEmpty(order.entities) ? [] : reduce(order.entities, (result, value) => {
result.push({ key: value.id, ...value });
return result;
}, []),
loading: order.loading
};
};
const mapDispatchToProps = {
loadOrders
};
export default connect(mapStateToProps, mapDispatchToProps)(OrderList);
the if part is false , but the onEndReached methods is still called, I must be insane.
the
Change this
onEndReachedThreshold={0.5}
to this:
onEndReachedThreshold={0}
Right now you're calling the end reached when you're halfway through. You can also try adding this to the FlatList:
legacyImplementation = {true}
If this still won't work I would recommend doing the 'pull' onRefresh. A nice example for you: https://www.youtube.com/watch?v=pHLFJs7jlI4
i met the problem too, in my case:
renderFooter somethings render null(height: 0) when loaded, but render ActivityIndicator when loading, and ActivityIndicator has its heigth bigger than 0(null's height)
when heigth change from 0 to ActivityIndicator's height, it will call onEndReached again
and you say the if part is false, i think its because it's not really false。
when code really run in FlatList, the if part is true, so it call onEndReached, and then the _scrollMetrics.contentLength or this._sentEndForContentLength has changed for some reason before your console in chrome. which makes the if part return false
above is all my thought for now, and i am still debugging for this problem, hope this answer will help you all
Using Expo SDK 29 for a react native application.
I would like to use a flat list component. This makes up the entirety of a SafeAreaView component. I make this point as there are lots of issues relating to a flat list inside of a scroll view which this is not.
The flat list shows a list of jobs.
I have added a jobLoading boolean to the redux state to manage when the list should show as refreshing and can confirm that this toggles as expected when firing the actions to fetch the data and the success.
When i add the props to the flat list for onRefresh and refreshing the component seems to work by showing the activity indicator in the UI but does not fire the onRefresh function. I have tried implementing the call in numerous ways but nothing happens. The result is that the activity indicator shows itself and never disappears.
As it's Expo SDK 29 the React Native version is 0.55.4
Anyone have any ideas of what to try. I've spent a couple of hours looking at this trying various things but suggestions are welcome.
Thanks in advance.
EDIT: Added the code for reference. Reducer for refreshing sets true when fetchJobs() is dispatched and false when a success or error is recieved. The console log for onRefresh never triggers.
import * as React from 'react'
import * as actions from '../../redux/actions'
import { ActivityIndicator, FlatList, KeyboardAvoidingView, Dimensions, SafeAreaView, StyleSheet, View } from 'react-native'
import { ApplicationState, JobState, Job } from '../../redux'
import { Button, Form, Input, Item, Text, Icon } from 'native-base'
import { JobListItem } from './jobListItem'
import { StateHandlerMap, compose, lifecycle, withPropsOnChange, withStateHandlers } from 'recompose'
import { connect } from 'react-redux'
interface ReduxStateProps {
jobs: JobState
refreshing: boolean
screenOrientation: string
}
interface ReduxDispatchProps {
fetchJobs: (param?: string) => any
}
export interface DataItem {
key: string
data: Job
}
interface ListProps {
jobList: DataItem[]
}
interface SearchStateProps {
timer: number | undefined
searchString: string
}
interface SearchHandlerProps extends StateHandlerMap<SearchStateProps> {
updateSearch: (searchString: string) => any
setTimer: (timer: number | undefined) => any
}
type OuterProps = {}
type InnerProps = OuterProps & ReduxStateProps & ReduxDispatchProps & ListProps & SearchStateProps & SearchHandlerProps
const enhance = compose<InnerProps, OuterProps>(
connect<ReduxStateProps, ReduxDispatchProps, OuterProps, ApplicationState>(
state => ({
jobs: state.job,
refreshing: state.jobLoading,
screenOrientation: state.screenOrientation
}),
dispatch => ({
fetchJobs: (param?: string) => dispatch(actions.jobs.request({ param }))
})
),
withPropsOnChange<ListProps, OuterProps & ReduxStateProps & ReduxDispatchProps>(
['jobs', 'screenOrientation'],
props => ({
jobList: props.jobs && Object.keys(props.jobs).map(job => ({ key: job, data: props.jobs[Number(job)] }))
})
),
withStateHandlers<SearchStateProps, SearchHandlerProps, OuterProps>(
{
timer: undefined,
searchString: ''
},
{
updateSearch: state => (searchString: string) => ({ searchString }),
setTimer: state => (timer: number | undefined) => ({ timer })
}
),
lifecycle<InnerProps, {}>({
componentDidMount() {
this.props.fetchJobs()
}
})
)
export const JobList = enhance(({ fetchJobs, jobList, refreshing, screenOrientation, searchString, setTimer, timer, updateSearch }) => {
const onSearchChange = (search: string) => {
clearTimeout(timer)
updateSearch(search)
const timing = setTimeout(() => {
fetchJobs(search)
}, 500)
setTimer(timing)
}
const onRefresh = () => {
console.log('requesting refresh')
fetchJobs()
}
return (
<SafeAreaView style={{ flex: 1}}>
<KeyboardAvoidingView style={{ flexDirection: 'row', justifyContent: 'space-evenly', paddingTop: 3, paddingRight: 3 }}>
<Form style={{ flex: 1, paddingLeft: 10, paddingRight: 10 }}>
<Item>
<Input
value={searchString}
onChangeText={(text: string) => onSearchChange(text)}
placeholder='Search'
/>
</Item>
</Form>
<Button onPress={() => {fetchJobs(); updateSearch('')}}>
<Icon name='refresh' />
</Button>
</KeyboardAvoidingView>
{refreshing &&
<View style={styles.refreshContainer}>
<Text style={{ paddingBottom: 10 }}>Fetching Data</Text>
<ActivityIndicator />
</View>
}
<FlatList
keyExtractor={item => item.key}
data={jobList}
renderItem={({ item }) =>
<JobListItem
screenOrientation={screenOrientation}
item={item}
/>
}
onRefresh={onRefresh}
refreshing={refreshing}
/>
</SafeAreaView>
)
})
const styles = StyleSheet.create({
refreshContainer: {
height: 60,
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
})
I'm having the exact same issue and I'm using expo SDK 30. But my case is a little bit different. The onRefresh function is called everytime I pull, however if I scroll down my list, and scroll back up fast, the loading indicator shows up, but my onRefresh function is not called.
My refreshing prop is set on my reducer, and my onRefresh function dispatches an action that fetches data and set refreshing true and false.
Here is my code:
class NoticiasScreen extends Component {
static navigationOptions = {
header: <Header
title='Notícias Alego'
leftComponent={<Image source={require('../../../assets/images/play_grande.png')} style={imageStyle} resizeMode='contain'/>}
/>
}
constructor(props) {
super(props);
this.renderItem = this.renderItem.bind(this);
this.keyExtractor = this.keyExtractor.bind(this);
this.renderContent = this.renderContent.bind(this);
this.navigateToNoticias = this.navigateToNoticias.bind(this);
this.carregarMaisNoticias = this.carregarMaisNoticias.bind(this);
this.onRefresh = this.onRefresh.bind(this);
}
componentDidMount() {
this.props.carregarNoticias(this.props.pagina);
}
renderItem({item}) {
return (
<NoticiaListItem noticia={item} abrirNoticia={this.navigateToNoticias} />
);
}
keyExtractor(item) {
return item.id.toString();
}
navigateToNoticias(noticia) {
this.props.navigation.navigate('NoticiasExibir', { id: noticia.id });
}
onRefresh() {
console.log('onRfresh');
this.props.carregarNoticias(1, true);
}
carregarMaisNoticias() {
const { carregarNoticias, pagina } = this.props;
carregarNoticias(pagina + 1);
}
renderContent() {
const { noticias, carregandoNoticias, erroNoticias } = this.props;
if(noticias.length === 0 && carregandoNoticias) {
return (
<View style={styles.containerCenter}>
<ActivityIndicator size="large" color={colors.verde}/>
</View>
);
}
if(erroNoticias) {
return (
<View style={styles.containerCenter}>
<Text style={styles.message}>{erroNoticias}</Text>
<TouchableOpacity hitSlop={hitSlop15}>
<Text>Recarregar</Text>
</TouchableOpacity>
</View>
)
}
return (
[<TextInput
style={styles.textInput}
placeholder='Pesquise'
key='pesquisa'
underlineColorAndroid='transparent'
/>,
<FlatList
data={noticias}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
style={styles.list}
key='lista'
onRefresh={this.onRefresh}
refreshing={carregandoNoticias}
onEndReached={this.carregarMaisNoticias}
onEndReachedThreshold={0.1}
/>]
)
}
render() {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
{this.renderContent()}
</View>
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return {
noticias: state.intranet.noticias,
pagina: state.intranet.pagina,
erroNoticias: state.intranet.erroNoticias,
carregandoNoticias: state.intranet.carregandoNoticias
}
}
function mapDispatchToProps(dispatch) {
return {
carregarNoticias: (pagina, recarregar) => dispatch(ActionCreator.carregarNoticias(pagina, recarregar))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NoticiasScreen);
No idea what's going on. Any help is appreciated.
EDIT:
I fixed it somehow. I added the onMomentScrollBegin prop to prevent my flatList from rendering twice on Render, and that fixed this issue.
here is what I added:
constructor(props) {
super(props);
...
this.onRefresh = this.onRefresh.bind(this);
this.onMomentumScrollBegin = this.onMomentumScrollBegin.bind(this);
this.onEndReachedCalledDuringMomentum = true; //PUT THIS HERE
}
onRefresh() {
this.props.carregarNoticias(1, true);
}
carregarMaisNoticias() {
if(!this.onEndReachedCalledDuringMomentum){
const { carregarNoticias, pagina } = this.props;
carregarNoticias(pagina + 1);
this.onEndReachedCalledDuringMomentum = true;
}
}
onMomentumScrollBegin() {
this.onEndReachedCalledDuringMomentum = false;
}
render() {
<OptimizedFlatList
data={noticias}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
style={styles.list}
key='lista'
onRefresh={this.onRefresh}
refreshing={carregandoNoticias}
onMomentumScrollBegin={this.onMomentumScrollBegin} //PUT THIS HERE
onEndReached={this.carregarMaisNoticias}
onEndReachedThreshold={0.1}
/>
}
I'm working on React native app. I'm looking for a searchable dropdown which I need to implement in many places.
Below see below video for reference:
Sample Video
I have implemented below third parties but they are not same as I need:
https://www.npmjs.com/package/react-native-searchable-dropdown
https://www.npmjs.com/package/react-native-searchable-selectbox
https://github.com/toystars/react-native-multiple-select
I tried implementing something similar a while ago and at the time I dropped the idea of having a drop down as it was inconsistent on both platforms & I could not find a perfect solution. I cannot see your video but I think I know where you're going with this.
Here is my advice:
I would create a separate screen that opens on the tap on this component that would be a 'dropdown', and in there create a searchable/filtrable list. You could try doing that using this: https://www.npmjs.com/package/searchable-flatlist, or create your own flatlist, which is super easy and allows for more customization!
EDIT:
If you don't want a separate screen use this: https://www.npmjs.com/package/react-native-searchable-dropdown
try implementing one :
const sports = ["Badminton","Cricket","Chess","Kho-Kho","Kabbadi","Hockey","Boxing","Football","Basketball","Volleyball","Tennis","Table Tennis"];
predict(){
let q = this.state.query;
let arr = sports.filter(ele => ele.toLowerCase().indexOf(q.toLowerCase()) != -1).splice(0,5);
this.setState((prev = this.state)=>{
let obj={};
Object.assign(obj,prev);
obj.predictions.splice(0,obj.predictions.length);
arr.forEach(ele=>{
obj.predictions.push({key : ele});
});
return obj;
});
}
<TouchableWithoutFeedback onPress={()=>{this.setState({done : true})}}>
<ScrollView
keyboardShouldPersistTaps='handled'
contentContainerStyle={style.content}
>
<View>
<TextInput
onChangeText={(text)=>{
this.setState({query : text , done : false});
this.predict();
}}
placeholder="What do you want to play ?"
placeholderTextColor="#A6A4A4"
value = {this.state.query}
onSubmitEditing = {()=>{this.setState({done : true})}}
underlineColorAndroid = "#0098fd"
></TextInput>
<TouchableOpacity onPress={()=>{this.filteredEvents()}}><Icon name="search" color = "#0098fd" size = {20}></Icon></TouchableOpacity>
</View>
{
this.state.predictions.length != 0 && !this.state.done &&
<FlatList
style={styles.predictor_view}
data={this.state.predictions}
extraData = {this.state}
renderItem = {
({item})=>(
<TouchableOpacity
style={styles.predictions}
onPress={()=>{
console.log('ok');
this.setState({query : item.key,done : true});
console.log(this.state);
}}>
<Text>{item.key}</Text>
</TouchableOpacity>
)
}
/>
}
</ScrollView>
</TouchableWithoutFeedback>
I have used react-native-autocomplete-input
I have written a component to help in the dropdown:
customDropDownComponent.js
/*This is an example of AutoComplete Input/ AutoSuggestion Input*/
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View, Button, ScrollView } from 'react-native';
//import all the components we are going to use.
import Autocomplete from 'react-native-autocomplete-input'
//import Autocomplete component
class CustomDropDownComponent extends Component {
constructor(props) {
super(props);
//Initialization of state
//films will contain the array of suggestion
//query will have the input from the autocomplete input
this.state = {
query: '',
data: [],
dataDuplicate:[],
itemSelected: false
};
}
componentDidMount() {
//Loading all data
this.loadData()
}
findElement(query, text, itemSelected) {
//method called everytime when we change the value of the inputquery === '' ||
if (itemSelected === true||query==='') {
//if the query is null or an item is selected then return blank
return [];
}
//making a case insensitive regular expression to get similar value from the data json
const regex = new RegExp(`${query.trim()}`, 'i');
//return the filtered data array according the query from the input
var newList = [];
var result = this.state.IATADup.filter(data => {
if (data.id.search(regex) === 0) {
newList.push(data);
return false;
} else {
return data.id.search(regex) >= 0;
}
});
result = newList.concat(result);
this.props.adjustMargin(1, result.length);//expadnding space in page to make room for dropdown
this.setState({ data: result, query: text, itemSelected: itemSelected });
}
loadData = () => {
var dataToLoad = Commondata.aircraftDetail
dataToLoad.sort(function (a, b) {
if (a.id > b.id) {
return 1;
}
if (b.id > a.id) {
return -1;
}
return 0;
});
this.setState({
dataDuplicate: dataToLoad
})
}
render() {
const { query } = this.state;
const data = this.state.data;
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
var inputContainerStyle = styles.inputContainerStyle;
if (this.props.destinationBorder === "#FF0000") {
inputContainerStyle = styles.inputContainerRedStyle;
}
return (
<View style={styles.container} >
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
flatListProps={{ nestedScrollEnabled: true }}
containerStyle={styles.autocompleteContainer}
listStyle={styles.listStyle}
inputContainerStyle={inputContainerStyle}
data={data}
keyExtractor={(item, i) => { return i }
defaultValue={query}
onChangeText={text => {
//handle input
if (text.trim() === "") this.props.adjustMarginBack();//adjust margin to normal in case of empty searrch element
this.findElement(text, text, false);//search for element
}}
placeholder={en.pick_one}
renderItem={({ item }) => (
//you can change the view you want to show in suggestion from here
<TouchableOpacity onPress={() => {
this.props.adjustMarginBack()
this.setState({ query: item.id, itemSelected: true, data: [] });
}}>
<Text style={styles.itemText}>
{item.id}
</Text>
<Text style={styles.itemSubText}>
{item.name}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF'
},
autocompleteContainer: {
backgroundColor: '#ffffff',
borderWidth: 0,
},
inputContainerStyle: {
borderWidth: 0.5, borderColor: '#D9D9D9', padding: '1.5%'
},
inputContainerRedStyle: {
borderWidth: 0.5, borderColor: '#FF0000', padding: '1.5%'
},
descriptionContainer: {
flex: 1,
justifyContent: 'center',
padding: '5%'
},
itemText: {
fontSize: 15,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
},
itemSubText: {
fontSize: 10,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
marginLeft: 10
},
infoText: {
textAlign: 'center',
fontSize: 16,
},
listStyle: {
height: 100,
position: "relative",
zIndex: 999
}
});
export default CustomComponent;
Now in the display screen:
app.js
import React, { Component } from 'react';
import { View, Text, ScrollView } from 'react-native';
import CustomDropDownComponent from './CustomDropDownComponent.js'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<View>
<ScrollView
nestedScrollEnabled={true}
keyboardShouldPersistTaps={'handled'}>
<CustomDropDownComponent /*Handle all inputs and margin resets as props *//>
</ScrollView>
</View>
);
}
}
How can I add a text input in React Native with the click of a button? For example, I would press the "+" button and it would add a text input at the bottom of the View.
EDITED:
Here is my code (deleted all the irrelevant stuff). Not working for some reason. Clicking the button doesn't do anything.
import React, { Component, PropTypes } from 'react';
import { StyleSheet,NavigatorIOS, Text, TextInput, View, Button,
TouchableHighlight, TouchableOpacity, ScrollView, findNodeHandle,
DatePickerIOS} from 'react-native';
import TextInputState from 'react-native/lib/TextInputState'
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {textInput: [],
date: new Date(),
}
}
addTextInput = (key) => {
let textInput = this.state.textInput;
textInput.push(<TextInput key={key} />);
this.setState({ textInput })
}
render(){
return(
<View>
<Button title='+' onPress={() =>
this.addTextInput(this.state.textInput.length)} />
{this.state.textInput.map((value, index) => {
return value
})}
</View>
)
}
}
this is an example for that :
import React, { Component } from 'react';
import { AppRegistry, View, Text, Button, TextInput} from 'react-native';
class App extends Component {
constructor(props){
super(props);
this.state = {
textInput : []
}
}
addTextInput = (key) => {
let textInput = this.state.textInput;
textInput.push(<TextInput key={key} />);
this.setState({ textInput })
}
render(){
return(
<View>
<Button title='+' onPress={() => this.addTextInput(this.state.textInput.length)} />
{this.state.textInput.map((value, index) => {
return value
})}
</View>
)
}
}
maybe that can help you :)
I have a solution that begins with a single text input. It has an "add" button that adds another text input just below the first. That new input keeps the "add" button, and all previous inputs above change to a "remove" button, with which, of course, the user can remove the corresponding view. I could only get it to work by handling state in a React Redux store, and so the code is spread out between too many different files to post here, but anyone interested can view it on GitHub or Snack.
I know this is an old post, but this is a problem I wish was answered when I first came here.
Here is example of dynamic add remove input
let obj = { text: '' }
this.state = {
attributeForm: [{ [1]: obj }],
duplicateAttributes: [1]
}
addAtributeRow() {
const { duplicateAttributes, attributeForm } = this.state;
let pushNumber = 1;
if (duplicateAttributes.length > 0) {
let max = Math.max(...duplicateAttributes);
pushNumber = max + 1
}
let arr = duplicateAttributes;
arr.push(pushNumber)
let obj = { text: '' }
this.setState({
attributeForm: [...attributeForm, { [pushNumber]: obj }]
})
this.setState({
duplicateAttributes: arr
})
}
deleteAttributeRow(number) {
const { duplicateAttributes, attributeForm } = this.state;
const index = duplicateAttributes.indexOf(number);
if (index > -1) {
duplicateAttributes.splice(index, 1);
let findedIndex;
for (let i = 0; i < attributeForm.length; i++) {
// var index = Object.keys(attributeForm[i]).indexOf(index);
if (Object.keys(attributeForm[i])[0] == number) {
findedIndex = i;
}
}
if (findedIndex > -1) {
attributeForm.splice(findedIndex, 1);
}
}
this.setState({
attributeForm: attributeForm,
duplicateAttributes: duplicateAttributes
})
}
render() {
const {attributeForm} = this.state;
{
duplicateAttributes.length > 0 && duplicateAttributes.map((item, index) =>
<View >
<Item style={GStyle.borderStyle} >
<Textarea placeholder="Text"
style={[GStyle.placeholder.text, { width: wp('90%') }]}
keyboardType="default"
autoCorrect={true}
autoCapitalize={'words'}
rowSpan={4}
value={attributeForm[index][item]['text']}
placeholderTextColor={GStyle.placeholder.color}
onChangeText={(text) => this.addAttributes(item, text, 'text')}
returnKeyLabel='done'
/>
</Item>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginHorizontal: wp('30%') }}>
{
<Button full rounded onPress={() => { this.deleteAttributeRow(item) }} >
<Icon name="minus" type="FontAwesome5" style={{ fontSize: wp('4%') }} />
</Button>
}
</View>
</View>
}
<Button full rounded onPress={() => { this.addAtributeRow() }} >
<Icon name="plus" type="FontAwesome5" style={{ fontSize: wp('4%') }} />
</Button>
}
If you want to do this with Hooks or Functional component then here is
the link of Expo
https://snack.expo.dev/#muhammadabdullahrishi/add-input
I have included how to add and delete Text Input
with hooks