I'm trying to show a FlatList, which is initially hidden, when the user clicks on a TextInput, but I'm getting an error saying that there are too many re-renders, take a look at the code:
const [state, setState] = useState ({
...
showFlatList: false,
})
return (
<ImageBackground source = {enterInfoBackGroundImage} style = {styles.container}>
<SafeAreaView>
<View style = {styles.backgroundArea}>
<TextInput style = {styles.inputText}
onFocus = {setState({showFlatList: true})}
autoCapitalize='characters'
placeholder = {'MAKE'}
placeholderTextColor = {'#B2B2B2'}
onChangeText = {text => setState({...state, companyName: text })}
value = {state.make}
/>
{state.showFlatList && <FlatList
style = {styles.tableView}
data = {make}
keyExtractor = {(item) => item.id}
renderItem = {({ item }) => (
<TouchableOpacity style = {styles.tableViewItem} onPress = {() => {
console.log(item.make, item.id)
}}>
<Text style = {styles.searchBarText}>{item.make}</Text>
</TouchableOpacity>
)}
/>}
</View>
</SafeAreaView>
</ImageBackground>
);
I'm only getting this error when I put {setState({showFlatList: true})} on onFocus, but when I put that inside onPress inside the TouchableOpacity, it worked, any kind of feedback is appreciated! :)
The problem is how you call setState on the onFocus property of your TextInput.
It should look more like this:
<TextInput
onFocus={() => {
setState({showFlatList: true});
}}
// ...
/>
So the same way you handled your TouchableOpacity's onPress.
Related
I would like to change the state from my value propreties, but my variable is a JSON.
import React, { useEffect, useState, useForm } from 'react';
import {
Button,
View,
Text,
FlatList,
TouchableOpacity,
ActivityIndicator,
Input,
} from 'react-native';
import { TextInput, Alert, Card, Title } from 'react-native-paper';
export default function Cadastrar({ route, titleMessage, messageField }) {
//pega o id dos parametros de rota, exportado no arquivo MESSAGECARD.JS na ação de clicar
const { id } = route.params;
const [DADOS, setData] = useState([]);
const getCardsMessages = async () => {
const response = await fetch(
`https://back-end.herokuapp.com/${id}`
);
const jsonObj = await response.json();
setData(jsonObj);
};
useEffect(() => {
getCardsMessages();
}, []);
const [titleMessage2, onChangeText] = useState("TESTE EVENT");
const [selectedId, setSelectedId] = useState(null);
const [search, setSearch] = useState('');
const updateSearch = (text) => {
setSearch(text);
};
return (
<View style={{ flex: 1, padding: 24 }}>
<FlatList
data={DADOS}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View>
<TextInput
value={"ID: "+item.id}
extraData={selectedId}
onPress={() => setSelectedId()}>
</TextInput>
<TextInput
placeholder="Titulo"
value={item.titleMessage}
onChangeText={(text) => updateSearch(text)}
></TextInput>
<TextInput value={item.messageField}>
</TextInput>
<TextInput
placeholder="Titulo"
value={titleMessage2}
onChangeText={onChangeText}>
</TextInput>
<Text style={{ margin: 10, padding: 10 }}>
<Button title="Atualizar Post" />
</Text>
</View>
)}
/>
</View>
);
}
I have trying this, but how can i get the value of these props which come from a json: value={item.titleMessage}
and update the:
const [search, setSearch] = useState('');
if goes this way it works:
value={search}
Anything will help, i starting my studies in react! Thanks a lot
Instead of calling updateSearch call your setSearch directly.
<TextInput
placeholder="Titulo"
value={search}
onChangeText={text => setSearch(text)}
/>
Well, at the start I didn't understood your question and I refactored your code to a map as we normally do in react thinking that it was what you want... Just to don't just delete it i'll keep it there to you, it wont be used to your question but you can refactor to your response of the search :) Also, its better to call your hooks in lower case. So change DADOS to dados.
export default function Cadastrar({ route, titleMessage, messageField }) {
...
if (DADOS[0]?.id != undefined) {
return (
<View style={{ flex: 1, padding: 24 }}>
{
DADOS.map(dado => {
return (
<View key={dado.id}>
<TextInput
value={"ID: " + dado.id}
extraData={selectedId}
onPress={() => setSelectedId()}
/>
<TextInput
placeholder="Titulo"
value={dado.titleMessage}
onChangeText={(text) => updateSearch(text)}
/>
<TextInput
value={dado.messageField}
/>
<Text style={{ margin: 10, padding: 10 }}>
<Button title="Atualizar Post" />
</Text>
</View>
)
})
}
</View>
);
} else {
return (
<div>DADOS não tem nada</div>
)
}
}
What I do there is, first check if the hook has data, if not will return a div saying that don't have any data, there you can put whatever you want. Then, if found data, I return your View with a map of the data inside.
To do the map you open a {}, that way you can write javascript inside the html, then you put your data to map, and for each data you return the component that you specify in the return.
Since you're returning many components you need some unique value to distinguish one to another, you do this by passing a unique key value to the element that wraps everything, in your case, other view. And since the id is a unique value, you pass the id in the key.
The rest is just put the values that you need to show in the components, beign it as props or writting the value inside the element.
I removed the titleMessage2 element since I assumed that it will be the 2nd id of your array of jsons.
well you can create a state variable to select one and other state variable to alternatives
const [search, setSearch] = useState(yourJson);
const [selected, setSelected] = useState('');
and now in your function updateSearch
const updateSearch =(newValue)=>{
setSelected(newValue)
}
and in the ui side you need to modify this
<TextInput
placeholder="title"
value={selected}
onChangeText={(text) => updateSearch(text)}
></TextInput>
So basically what I said in the title, I am simply trying to change a prop that im passing to the component of Post if the item currently being rendered is in the viewport.
I am getting double the output like its firing twice, and its not even correct.
im comparing a key to the id (same thing) and if the 'activeVideo !== item.id' (video id's) are the same
the video should play, because i pass 'False' to the 'paused' property.
question is why am i getting double the output and why are both videos being paused when one of them clearly shouldnt>?
Need help fast, its for a school project.
Home.js
const [activeVideo, setActiveVideo] = useState(null);
const onViewRef = React.useRef((viewableItems)=> {
setActiveVideo(viewableItems.changed[0].key)
})
const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 75 })
return (
<View>
<FlatList
data={posts}
showsVerticalScrollIndicator={false}
snapToInterval={Dimensions.get('window').height - 24}
snapToAlignment={'start'}
decelerationRate={'fast'}
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
renderItem={({item}) => <Post
post={item}
paused={activeVideo !== item.id}
/>}
/>
</View>
)}
Post.js
const Post = (props) => {
const [post, setPost] = useState(props.post);
const [paused, setPaused] = useState(props.paused);
console.log(props.paused)
const onPlayPausePress = () => {
setPaused(!paused);
};
const onPlayPausePress2 = () => {
setPaused(!paused);
};
return (
<View style={styles.container}>
<TouchableWithoutFeedback>
<Image
source={{uri: post.poster}}
style={styles.video2}
/>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={onPlayPausePress}>
<Video
source={post.postUrl}
style={styles.video}
onError={(e)=> console.log(e)}
resizeMode={'cover'}
repeat
paused={paused}
/>
</TouchableWithoutFeedback>
</View>
)}
I was having issues with something like this (specifically in the TextInput value attribute):
const Stuff = props => {
const [items, setItems] = useState([]);
const handleNewItem = () => {
setItems([...items, '']);
};
const handleText = (text, index) => {
items[index] = text;
setItems(items);
// this was populating correctly in console.log
// as I type, it will come out like ["somedata", "blah"....] etc...
};
return (
<>
<View style={{marginTop: 20}}>
<View>
{items.map((items, index) => {
return (
<View key={index}>
<Text>{index + 1}</Text>
// issue with setting value attribute
// Text would disappear as I type in the input field
<TextInput value={items} onChangeText={text => handleText(text, index)} />
</View>
);
})}
<TouchableOpacity onPress={e => handleNewItem(e)}>
<Text>Add item</Text>
</TouchableOpacity>
</View>
</View>
</>
);
};
I was able to get console logged out the correct values for items, but on my mobile simulator, when I type something, the text disappears.
When I removed value={items} from the TextInput component, I'm able to type in the simulator input field, without the text disappearing. I always thought we needed a value from reactjs. Do we not need this? or am I doing something wrong?
I would suggest don't directly update your state. Instead use new object to update the state like
const handleText = (text, index) => {
let newItems = [...items];
newItems[index] = text;
setItems(newItems);
};
I have a component that renders the input field, I want to pass the data to the next page when user clicks on "next" button in the header. What is the best practice for doing so? How do I expose this into Page.navigationOptions?
Or is it best to just set up redux for these types of things?
const Page = () => {
const [desc, getDesc] = useState('');
return (
<View style={styles.inputFieldDescContainer}>
<TextInput
multiline
placeholder='Write a description...'
onChangeText={(text) => getDesc(text)}
value={desc}
/>
</View>
);
};
// How do I pass desc properties down into navigationOptions?
Page.navigationOptions = (navData) => {
return {
headerTitle: 'Page,
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Next'
onPress={() => {
navData.navigation.navigate('NextPage', {data: navData});
}}
/>
</HeaderButtons>
),
headerBackTitle: null
};
};
/* NextPage.js */
const NextPage = (props) => {
console.log('enter props data', props.navigation.getParam('data'));
console.log('enter props navigation', props.navigation);
const [valueText, setValueText] = useState();
return (
<View>
<TextInput onChangeText={(text) => setValueText(text)} value={valueText}/>
<TouchableOpacity><Text>Create your workout</Text></TouchableOpacity>
</View>
);
;}
Sharing state and props between component and options is possible in React Navigation 5 https://blog.expo.io/announcing-react-navigation-5-0-bd9e5d45569e
In React Navigation 4, you can use params to store the value to be able to share it:
const Page = ({ navigation }) => {
const desc = navigation.getParam('description', '');
return (
<View style={styles.inputFieldDescContainer}>
<TextInput
multiline
placeholder='Write a description...'
onChangeText={(text) => navigation.setParams({ description: text )}
value={desc}
/>
</View>
);
}
I use a RootForm as the basic template for form page. There is one field associated with location autocomplete, so I wrap the native autocomplete of react-native and use it in that field. However, the autocomplete dropdown list is blocked by other fields in the form which are rendered behind it. I try to search online but no useful materials. Using modal or zIndex is not the solution here. How could I make the dropdown list on the top of other components even if it renders earlier than other components?
The following two snippets are my rootform and autocomplete render functions.
render() {
const { input } = this.state;
const cities = this.state.cities;
return (
<View style={styles.container}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
data={cities}
defaultValue={input}
onChangeText={text => this.setState({ input: text })}
placeholder="Enter Location"
renderItem={({ cityName, cityId }) => (
<TouchableOpacity style={styles.listStyle} onPress={() => this.setState({ input: cityName, cities: [] })}>
<Text style={styles.itemText}>
{cityName}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
render() {
const data = this.props.data;
let fields = [];
let onPress = null;
Object.keys(data).forEach((key, index) => {
let options = data[key].options ?
data[key].options : null
if ("type" in data[key]) {
fields.push(
<View style={styles.formField} key={key}>
<Text style={styles.text}>{data[key].label}</Text>
<AutoComplete />
</View>
)
} else {
let custom = [styles.formField];
if (options) {
fields.push(
<View style={custom} key={key}>
<Text style={styles.text}>{data[key].label}</Text>
<TextInput value={data[key].value} style={styles.input}
readOnly
{...options} />
</View>
)
} else {
fields.push(
<View style={custom} key={key}>
<Text style={styles.text}>{data[key].label}</Text>
<TextInput value={data[key].value} style={styles.input}
onChangeText={(text) => this.props.onFieldChange(key, text)}
{...options} />
</View>
);
}
}
})
return (
<KeyboardAwareScrollView style={styles.container}>
{fields}
</KeyboardAwareScrollView>
)
}
You can just change your style.container to have a higher zIndex than whatever is appearing on top of it. However this will have the other items in the form appear behind the area reserved for the dropdown list, and render them unselectable.
If you want items underneath the Autocomplete component's area to still be interactive/selectable, you can use React.useState in order to have a dynamic zIndex property on your component.
const [componentZIndex, setComponentZIndex] = React.useState(1);
You will want your components behind the area reserved for the list to have a zIndex higher than 2 so that they are interactive.
Then you'll want to render your own input component so that you have access to the onFocus property. Luckily, the library you are using allows you to do this:
renderTextInput={() => (
<View style={styles.input}>
<TextInput
onFocus={() => setComponentZIndex(999)}
value={value}
/>
</View>
)}
This will bring the list to the top whenever the user is using the autocomplete component. The last thing to do is to make sure that you push it to the back once the user is no longer using it.
React.useEffect(() => {
Keyboard.addListener('keyboardDidHide', _keyboardDidHide);
return () => {
Keyboard.removeListener('keyboardDidHide', _keyboardDidHide);
};
}, []);
const _keyboardDidHide = () => {
setComponentZIndex(1)
};