Whenever I tried to build UI from scratch I'm getting this error adjacent jsx element must be wrapped in an enclosing tag. I don't know how to solve this. Because I tried different methods, I've tried to put the blocks within View component withflex:1 but non-use. Is there any proper solution for this. This is becoming a great challenge for me because I can't design any components of my own. What to do please help me. Following is my code.
screen.js
export default class FirstScreen extends Component {
constructor(props){
super(props);
this.state = {
showPopupDialog: false,
workType: "",
workers: []
}
}
componentWillMount(){
fetch('http://192.168.1.6:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (workType) => {
console.log(workType);
}
popupDialog = (id, workType) => {
this.setState ({
showPopupDialog: true,
workType: workType
});
//make sure you set showPopupDialog to false and workType to "" when you click yes or no button in PopupDialog component so that it will work the next time you click on card
}
render() {
const { workers, workType, showPopupDialog} = this.state;
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{workers.map((a, index)=> (
<View style={{flex:1}}>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog(a.id, a.work_type)}>
<View style={{ maringTop: 10, marginLeft:120}}>
<Image style={{ height: 100, width: 100 }} source={{ uri: a.work_type == 'Carpenter' ? images[0].image : images[1].image}}/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
</View>
))}
{showPopupDialog && <PopupDialog
dialogStyle={{ backgroundColor: "#FFFFFF", height: 180, width:300, borderWidth:1,padding:10}}
overlayBackgroundColor="#fff" dismissOnTouchOutside={true}>
<View style={styles.dialogContentView}>
<Text style={{fontSize:18, margingTop:10,color:"#000000"}}>Are you sure you want to submit?</Text>
<View style={{flexDirection:'row'}}>
<View style={styles.button_1}>
<Button title="Yes" color="#FF6633" onPress={() => this.onPressYes(workType)}/>
</View>
<View style={styles.button_1}>
<Button title="No" color="#FF6633" onPress={() =>this._onPressNo() }/>
</View>
</View>
</View>
</PopupDialog>}
</ScrollView>
</View>
);
}
}
The issue I'm facing is I can't place the <PopupDialog> component adjacent to <CardSection> , in order to that I put the <PopupDialog> within <View> ,even though it doesn't solve my issue.Please help..Please
Give a try with below corrected code.
There are two things that needs be corrected
You are doing .map but you are not returning anything which I have
corrected in the code below
export default class FirstScreen extends Component {
constructor(props){
super(props);
this.state = {
workType: "",
workers: []
}
}
componentWillMount(){
fetch('http://192.168.1.6:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (workType) => {
console.log(workType);
}
popUpDialog = (id, workType) => {
this.setState ({
workType: workType
});
this.popupDialog.show();
//make sure you set workType to "" when you click yes or no button in PopupDialog component so that it will work the next time you click on card
}
render() {
const { workers, workType} = this.state;
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{workers.map((a, index)=> (
<View style={{flex:1}}>
<CardSection>
<TouchableOpacity onPress={() => this.popUpDialog(a.id, a.work_type)}>
<View style={{ maringTop: 10, marginLeft:120}}>
<Image style={{ height: 100, width: 100 }} source={{ uri: a.work_type == 'Carpenter' ? images[0].image : images[1].image}}/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
</View>
))}
<PopupDialog ref={popupDialog => {
this.popupDialog = popupDialog;
}}
dialogStyle={{ backgroundColor: "#FFFFFF", height: 180, width:300, borderWidth:1,padding:10}}
overlayBackgroundColor="#fff" dismissOnTouchOutside={true}>
<View style={styles.dialogContentView}>
<Text style={{fontSize:18, margingTop:10,color:"#000000"}}>Are you sure you want to submit?</Text>
<View style={{flexDirection:'row'}}>
<View style={styles.button_1}>
<Button title="Yes" color="#FF6633" onPress={() => this.onPressYes(workType)}/>
</View>
<View style={styles.button_1}>
<Button title="No" color="#FF6633" onPress={() =>this._onPressNo() }/>
</View>
</View>
</View>
</PopupDialog>
</ScrollView>
</View>
);
}
}
If I understand your question correctly...
You can return multiple root elements in jsx by wrapping is in a <React.Fragment> element (you can use <> and </> in v16.2 and later). Fragments are new in React v16. Prior to that, you just have to wrap them in some element (a div or span, usually).
The problem is that you have this structure:
<a>
{this.state.workers.map((a, index)=>
<b/>
<c/>
)}
</a>
Since <b/><c/> is parsed separately and there's no enclosing element, you're getting the error. But an enclosing element isn't necessary for the final structure, which does have an enclosing element. The solution is to simply return an array of JSX elements, like this:
<a>
{this.state.workers.map((a, index)=>
[<b/>,
<c/>]
)}
</a>
Related
I'm building a basic nutrition app that shows user's info regarding items they searched.
However, when my user selects an item from the flat list, I have a modal component that pops up and shows more info regarding the item. However, when I press back on my modal and select a new item on the FlatList, the data is remaining the same from the first item I pressed :(
export default class Tracker extends React.Component {
static navigationOptions = {
title: "Tracker",
};
//storing results from the api into this local state
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null,
show: false,
};
}
`
fetchData = (item) => {
console.log(item);
fetch(
`https://api.edamam.com/api/food-database/parser?
ingr=${item}&app_id=${APP_ID}&app_key=${APP_KEY}`
)
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
// console.log(responseJson.hints[1].food.nutrients);
this.setState({
// passing in all the hints info into itemArray, which contains all the info regarding
the items
itemArray: responseJson.hints,
});
})
.catch((error) => {
console.log(error);
});
// dimisses keyboard if they press the button on the screen
Keyboard.dismiss();
};
<Button
title="Search"
onPress={() => this.fetchData(this.state.item)}
/>``
<View style={styles.ViewFilterContainer}>
<TouchableOpacity style={styles.ViewFilterContainer}>
<View style={styles.filterButtonView}>
<Text style={styles.filterText}> Filter </Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.paddingForResultsContainer}>
<FlatList
style={styles.resultsBackground}
data={this.state.itemArray}
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => this.setState({
show: true
})} //() => navigate("foodInfo")
>
<View style={styles.resultsContainer}>
<View style={styles.textView}>
<Text style={styles.resultsText}>
{item.food.label}
{item.food.brand}
</Text>
</View>
<View style={styles.nutritionResultsText}>
<Text style={styles.resultsTextSubInfo}>
F: {Math.round(item.food.nutrients.FAT)}
</Text>
<Text style={styles.resultsTextSubInfo}>
C: {Math.round(item.food.nutrients.CHOCDF)}
</Text>
<Text style={styles.resultsTextSubInfo}>
P: {Math.round(item.food.nutrients.PROCNT)}
</Text>
<Text style={styles.resultsTextSubInfo}>
K/Cal: {Math.round(item.food.nutrients.ENERC_KCAL)}
</Text>
</View>
</View>
<Modal transparent={true} visible={this.state.show}>
<View style={styles.modalView}>
<View>
<Text>{item.food.brand}</Text>
</View>
<Button title="Back" onPress={() => this.setState({show:false})}/>
</View>
</Modal>
</TouchableOpacity>
)}
/>
</View>
I'm a beginner in React Native and struggling in passing and executing functions as props from parent to child component. Here's the code:
MainMap
import React from 'react';
import {
TouchableWithoutFeedback,
StyleSheet,
View,
Button,
FlatList,
Dimensions
} from 'react-native';
import PlaceInput from '../components/PlaceInput';
const INCREMENT = 1;
const HEIGHT = Dimensions.get('window').height
const WIDTH = Dimensions.get('window').width
class MainMap extends React.Component{
constructor(props){
super(props);
this.state={
numOfInput:[],
counter: 0,
}
this.onAddSearch = this.onAddSearch.bind(this)
this.onDeleteSearch = this.onDeleteSearch.bind(this)
}
onAddSearch(){
this.setState((state) => ({
counter: state.counter + INCREMENT,
numOfInput: [...state.numOfInput, state.counter]
}))
}
onDeleteSearch(inputId){
const items = this.state.numOfInput.filter(item => item.id !== inputId)
this.setState({
numOfInput: items
})
}
render(){
return(
<TouchableWithoutFeedback onPress={this.hideKeyboard} >
<View style={styles.container} >
<Button title='Add a location' onPress={this.onAddSearch} />
<View style={{height: HEIGHT/2 }}>
<FlatList
data={this.state.numOfInput}
keyExtractor={(item, index) => item.id}
renderItem={itemData => {
return(
<PlaceInput
key={itemData.item.id}
// id={itemData.item.id}
onDelete={this.onDeleteSearch}
showDirectionOnMap={this.showDirectionOnMap}
userLatitude={userLatitude}
userLongitude={userLongitude}
/>
)
}}
/>
</View>
</View>
</TouchableWithoutFeedback>
)
}
}
export default MainMap;
const styles = StyleSheet.create({
container:{
flex: 1
},
})
Here's the PlaceInput component
class PlaceInput extends React.Component{
constructor(props){
super(props);
... // These lines have no relation to what I'm asking so don't mind them
}
...
render(){
return(
<View style={styles.buttonContainer} >
<View style={{flex: 1, alignItems: 'center'}}>
<Text style={{fontSize: 8}}>{'\u25A0'}</Text>
</View>
<View style={{flex: 4}}>
<TextInput
autoCorrect={false}
autoCapitalize='none'
style={styles.inputStyle}
placeholder='Search your places'
onChangeText={(input) => {
this.setState({destinationInput: input});
this.getPlacesDebounced(input);
}}
value={this.state.destinationInput}
/>
{/* {predictions} */}
</View>
<View style={styles.rightCol}>
<TouchableOpacity onPress={this.props.onDelete.bind(this, this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
</View>
</View>
)
}
}
What I'm trying to do:
Define a function to execute in MainMap.js (in FlatList --> PlaceInput for specific) , which is to delete an search bar( the whole PlaceInput in the FlatList) every time I click the right symbol of that search bar. The function is onDeleteSearch
The right symbol is styled in a TouachableOpacity as you can see in the PlaceInput.js component. I put it in the last View pair
However, When I click, the screen deletes all the search bars, not the one I click. Is it the problem of the id of the component PlaceInput ? Or with the way I call the props?...
Please help me !
<TouchableOpacity onPress={this.props.onDelete.bind(this, this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
Don't bind, just call this.props.onDelete(this.props.id);
In MainMap, try this:
<PlaceInput
key={itemData.item.id}
// id={itemData.item.id}
onDelete={() => this.onDeleteSearch(itemData.item.id)} // here
showDirectionOnMap={this.showDirectionOnMap}
userLatitude={userLatitude}
userLongitude={userLongitude}
/>
Assuming the function:
onPressed(optionalArgument = false) {
// do something
}
You can pass a function to onPress if it does not require any arguments, i.e
onPress={onPressed} // - would work if no arguments required.
onPress={onPressed(argument)} // - will get fired on component render
onPress={()=> onPressed(argument)} // - will work as expected on button press
onPress={()=> { // - will work as expected on button press
// Multiple lines of code
onPressed(argument);
anotherFunction();
}
};
In your MainMap you are doing everything correctly, just uncomment the
// id={itemdata.item.id}
In PlaceInput, just one small change:
<TouchableOpacity onPress={() => this.props.onDelete(this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
If you don't add ()=> to your onPress, the function gets called immediately, that's why you see such behaviour.
Your numOfInput is just a list of numbers, so instead of using item.id-s use item directly.
Here:
const items = this.state.numOfInput.filter(item => item !== inputId)
And here
<PlaceInput
key={itemData.item}
// id={itemData.item}
...
/>
Have parent tag as <TouchableWithoutFeedback> and child as <Image>, trying to change the image source from the parent onPress function, but getting error. how can I archive this using refs? or any other way to resolve it?
React-native version : 0.56
Error :
Cannot read property 'favIcon' of undefined
Sample Code :
const fav = require('../../images/favorite.png');
const nonfav = require('../../images/not_favorite.png');
updateFavorite = (id) => {
this.props.refs.favIcon.source(fav);
}
render(){
return (
<View style={styles.container}>
<TouchableWithoutFeedback onPress={ this.updateFavorite.bind(this, 1) }>
<View style={ styles.imageView }>
<Image refs="favIcon" source={ nonfav } />
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={ this.updateFavorite.bind(this, 2) }>
<View style={ styles.imageView }>
<Image refs="favIcon" source={ nonfav } />
</View>
</TouchableWithoutFeedback>
</View>
)
}
Note : change only the child image of onPressed, not all images.
try it using nativeProps, like
import resolveAssetSource from 'resolveAssetSource';
this.refs['favIcon'].setNativeProps({
src: [resolveAssetSource(fav)]
});
refer this for more.
It might not work with react-native 0.50+ versions but according to this comment it should work with 0.57.1
Updating properties of elements in React is best done by calling setState method. Try the following
updateFavorite = (event) => {
event.target.setAttribute('src', fav)
}
render(){
return (
<View style={styles.container}>
<TouchableWithoutFeedback>
<View style={ styles.imageView }>
<img ref={ref => this.favIcon = ref} src={nonFav} onPress={ this.updateFavorite }/>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback>
<View style={ styles.imageView }>
<img ref={ref => this.favIcon = ref} src={nonFav} onPress={ this.updateFavorite } />
</View>
</TouchableWithoutFeedback>
</View>
)
}
I have created amenitiesArray which has elements like club_house, swimming_pool, etc. I am mapping over this amenitiesArray using .map() but when I pass it as a prop I am not able to display it on screen why so?
console.log(amenitiesArray) is:
0:"Gymnasium"
1:"swimming_pool"
2:"club_house"
3:"childrens_play_area"
4:"multipurpose_room"
5:"security"
6:"jogging_track"
7:"power_backup"
8:"landscaped_gardens"
9:"car_parking"
Code:
const DisplayAmenities = ({ name }) => (
<Text style={{ color: 'black', fontSize: 16 }}>{name}</Text>
);
export default class Project extends Component {
render() {
let amenitiesArray = [];
for (var key in amenities[0]) {
if (amenities[0][key] === 'Yes') {
amenitiesArray.push(key);
}
}
console.log(amenitiesArray);
return (
<ScrollView>
<View style={{ marginTop: 20 }}>
{amenitiesArray.map(val => {
<DisplayAmenities name={val} />;
})}
</View>
</ScrollView>
);
}
}
Above code does not display anything on screen? Why so?
Because you forgot to return the component. Try to fix like this:
return (
<ScrollView>
<View style={{ marginTop: 20 }}>
{amenitiesArray.map((val,index) => {
return <DisplayAmenities key={index} name={val} />; //fixed
})}
</View>
</ScrollView>
);
For short, you could omit the curly brackets, like so
return (
<ScrollView>
<View style={{ marginTop: 20 }}>
{amenitiesArray.map((val,index) => <DisplayAmenities key={index} name={val} /> )}
</View>
</ScrollView>
);
{amenitiesArray.map(val => {
<DisplayAmenities name={val} />;
})}
lacks explicit return, map callback function maps an array to an array of undefined.
For implicit arrow return, it should be:
{amenitiesArray.map(val =>
<DisplayAmenities name={val} />;
)}
The use of array-callback-return ESLint rule allows to avoid this sort of mistakes.
I am trying to pass a data (server response) as an argument of a button.Actually in my case there are certain type of workers (listing within cards ). If clicked on a worker it should be saved to db with the corresponding worker's id.Upon clicking on the card of workers there will be a popup showing for confirmation.So if clicked on yes button I'm taking the corresponding worker's id and perform another fetch request for saving it to my db.But this is not working I'm confused how to pass arguments within onclick property of a button and take that argument within fetch method.Following is my code.I'm pasting only a portion of my code below.
updated code
export default class FirstScreen extends Component {
constructor(){
super();
this.state = {
workers: [],
}
}
componentWillMount(){
fetch('http://192.168.1.3:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (worker_id) => {
fetch('http://192.168.1.3:3000/api/work_detail',{
method:'POST',
headers:{
Accept:'application/json'
},
body:JSON.stringify({
worker_id
})
})
}
render() {
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{this.state.workers.map((a, index)=>
<Container>
<CardSection>
<TouchableOpacity
onPress={() => this.popupDialog.show()}
>
<View style={{ maringTop: 10, marginLeft:120}}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: a.work_type == 'Carpenter' ? images[0].image : images[1].image}}
/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
</Container>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() =>console.log('Clicked')}>
<Button
backgroundColor="#FF4500"
title='View Status' />
</TouchableOpacity>
</View>
</ScrollView>
<PopupDialog
ref={popupDialog => {
this.popupDialog = popupDialog;
}}
dialogStyle={{ backgroundColor: "#FFFFFF", height: 180, width:300, borderWidth:1,padding:10}}
overlayBackgroundColor="#fff"
dismissOnTouchOutside={true}
>
<View style={styles.dialogContentView}>
<Text style={{fontSize:18, margingTop:10,color:"#000000"}}>Are you sure you want to submit?</Text>
<View style={{flexDirection:'row'}}>
<View style={styles.button_1}>
<Button
title="Yes"
color="#FF6633"
onPress={() => this.onPressYes(worker_id)}
/>
</View>
<View style={styles.button_1}>
<Button
title="No"
color="#FF6633"
onPress={() =>this._onPressNo() }
/>
</View>
</View>
</View>
</PopupDialog>
</View>
})
);
}
}
workers is the array I'm fetching from server.
Can you try replacing _onPressYes = (a.worker_id) with with _onPressYes = (worker_id) and then
body:JSON.stringify({
worker_id
})
Let me know if that helps.
What I usually do is return a function with the given parameter. I mean wrap a function in a function like the following:
onClickHandler = (value) => () => {
// do whathever you want.
};
<Component onClick={this.onClickHandler(yourValue)}/>
Because the problem in your code is that the function will be execute without calling the onClick event because when you pass the argument to a simple function you are already calling it.
I hope it can help you :)