Error in Nested map function React Native - react-native

I can't achieve to have two nested map with this below code i got [Error: Text strings must be rendered within a <Text> component.]
const MyScreen = ({ route, navigation }) => {
const { fieldsBlock } = route.params;
// Iterate
const fieldItems = fieldsBlock.map((fieldBlock) => (
<View>
<Text>{fieldBlock.title}</Text>
{fieldBlock.fields.map((field) => (
<Text>{field.fieldValue}</Text>
))}
</View>
)
);
return (
<View>
{fieldItems}
</View>
);
};
Only if i use one map function then it works but with the nested map function it give an error.

I think, that the problem is that if fieldItems results in an empty array, you will be trying to render nothing between View tags.
I suggest to change your code to render null if arrays are empty, like this
const MyScreen = ({ route, navigation }) => {
const { fieldsBlock } = route.params;
// Iterate
const fieldItems = fieldsBlock.map((fieldBlock) => (
<View>
<Text>{fieldBlock.title}</Text> // <-- NOTE: because of this, view is not empty
{(fieldBlock && fieldBlock.fields && fieldBlock.fields.length)
? fieldBlock.fields.map((field) => (
<Text>{field.fieldValue}</Text>
))
: null
}
</View>
)
);
return (
{fieldItems && fieldItems.length)
? <View>{fieldItems}</View> : null // <-- NOTE: In this case avoid render a view without children
}
);
};
It is fine to render this:
<View />
but not this:
<View>
</View>
Cheers!

Related

My if statement is not working in React Native

I want to build a search bar that filters a flatlist in react native. I'm doing so with a TextInput and a component SearchFilter.
In my homescreen I have this TextInput:
<TextInput
value={input}
onChangeText={(text) => setInput(text)}
style={{ fontSize: 16, marginLeft: 10 }}
placeholder="Search"
/>
And this component:
<SearchFilter data={Cars} input={input} setInput={setInput} />
In the component file I have my flatlist:
const searchFilter = (data, input, setInput) => {
console.log(input)
return (
<View>
<Text>SearchFilter</Text>
<FlatList
style={styles.list}
data={data}
renderItem={({ item }) => {
if (input === "") {
return (
<View>
<Text>test</Text>
</View>
)
}
}}
></FlatList>
</View>
);
};
When nothing is being searched I want test to be displayed.
The problem is that it shows nothing.
When I do a console.log(input) in my homescreen the console returns an emty string
but when I do a console.log(input) in my component file it returns {}. I do not know why. When I tried
if (input === " {}") {
return (
<View>
<Text>test</Text>
</View>
)
}
it also did not work.
Any asolutions?
I suppose the searchFilter is your component ?
If it is the case then you don't use the props correctly, try like this :
const SearchFilter = ({data, input, setInput}) => { ... rest of your code ... }
You can't compare a object like this, it's not the same (in the memory).
Assuming var x = {}
x == {} // false (it's the same 'content' but it's not saved at the same place in the memory
x == "{}" // false (x is a object "{}" is a string)`
Assuming var y = x
y == x // true
To compare basic object, you can use JSON.stringify() function, it's parse object to string like this : (JSON.stringify(x) == JSON.stringify({})) === true
It's explain why your condition doesn't work but I don't know why do you have a object as output (I'm not a react developer ^^)
I hope it's even be usefull for you

react-native : how to sort data before screen is drawn?

I call data by using useQuery and gql.
const SEE_ALL_FEED_ORDER = gql`
query seeAllFeedOrder {
seeAllFeedOrder {
id
name
avatar
directFeedNumber
}
}
`;
const { data: allFeedData, loading: allFeedDataLoading,
refetch: allFeedRefetch } = useQuery(SEE_ALL_FEED_ORDER);
I named result data as allFeedData as above.
And I need to sort this allFeedData before screen is shown up.
So I use useEffect and useState.
const [flatlistdata, setFlatlistdata] = useState([]);
useEffect(() => {
if (!allFeedDataLoading) {
setFlatlistdata(
[...allFeedData.seeAllFeedOrder].sort(function (a, b) {
return b.directFeedNumber - a.directFeedNumber;
})
);
}
}, []);
So if this query loading is finished, then I put sorted data to flatlistdata by using setFlatlistdata.
And with this flatlistdata, I run flatlist.
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/>
But when I click screen, undefined is not an object(evaluating 'allFeedData.seeAllFeedOrder' error comes.
which means I couldn't call allFeedData.
I think this might happen screen is drawn before data is sorted? is that right?
So I also give condition to Flatlist as below.
flatlistdata === [] ? (
<View>
<ActivityIndicator size={30}></ActivityIndicator>
</View>
) :
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/> }
or
allFeedDataLoading ? (
<View>
<ActivityIndicator size={30}></ActivityIndicator>
</View>
) :
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/> }
I intended to draw ActivityIndicator screen first before data is sorted and put to flatlistdata and then proper screen show up, but it throws same error.
What is the problem? and how can I fix this ?
So I use useEffect and useState.
why would you need that? the sorted data is derived state that doesn't need to be managed separately:
const { data: allFeedData } = useQuery(SEE_ALL_FEED_ORDER);
const sortedData = allFeedData ? [...allFeedData.seeAllFeedOrder].sort(...) : []
then just pass sortedData to the FlatList. If it turns out that sorting is slow or that referential identity is important, you can wrap the sorting in useMemo. I also have a blog post on this topic: https://tkdodo.eu/blog/dont-over-use-state

React native while adding one item id by onpress it adds all the item at once

can anyone tell me what can i do..i want to change the add button to remove button when one item added and again onpress remove ...add button will come..
function //
const [multipletest, setmultipleTest] = React.useState([]);
function addTest(crypto) {
setmultipleTest((state) => [...state, crypto.id]);}
map list //
{filterdData.map((crypto, index) => (
<View key={index.toString()}>
<TouchableOpacity onPress={() => addTest(crypto)}>
{console.log(crypto, "crypto")}
<Text> Add</Text>
</TouchableOpacity>
</View>
This logic might help
function Example() {
// default state
const [multipletest, setmultipleTest] = React.useState([]);
const add = (crypto) => {
let newData = [...multipletest];
newData.push(crypto.id);
setmultipleTest(newData);
}
const remove = (crypto) => {
let newData = [...multipletest];
newData = newData.filter((e) => e !== crypto.id);
setmultipleTest(newData);
}
// map list
return filterdData.map((crypto, index) => {
// check crypto id already exist or not
const exists = multipletest.includes(crypto.id);
return (
<View key={index.toString()}>
<TouchableOpacity
onPress={() => {
// handle onpress either add or remove as per condition
exists ? remove(crypto) : add(crypto);
}}
>
<Text>{exists ? "Remove" : "Add"}</Text>
</TouchableOpacity>
</View>
);
});
}

how to render item only if there is data?

I am using axios to get json data from a link , I am calling that link from componentWillMount function , and then I store data in the state ,
my render method contains a FlatList that should display that data , all works fine , I only want that I can make a condition , I want my render method to display Faltlist only if there is some data in the state otherwise display a simple message saying that there is no data to show ,
I dont know why it didn't work with my code , could please help :
this is my flatlist in render method :
<FlatList
data = {this.state.data}
renderItem = {this.renders}
keyExtractor ={this.keyextractor}
/>
and this is the renders function that is called by flatlist :
renders({item}){
if(item.length > 0)
{
return(
<Itemz
offer={item}
/>
);
}else{
return(
<View><Text>No Data to show</Text></View>
);
}
you can try to use FlatList prop ListEmptyComponent, Rendered when the data is empty.
https://facebook.github.io/react-native/docs/flatlist#listemptycomponent
You need to put your condition check in the render() function, since you only want to show the FlatList when data is present.
render() {
const {data} = this.state;
if (data.length > 0) {
return <FlatList data={data} renderItem={this.renderItem} ... />
} else {
return (
<View>
<Text>No Data to show</Text>
</View>
);
}
}
renderItem = ({item}) => {
return <Item offer={item} />
}

React Native - how to use map function on an object

I'm creating an APP that get some data from fetch function. No problem here. The array has the data correctly. I'm doing it like this:
constructor(){
super()
this.state = {
fetching: false,
api: []
}
}
componentWillMount(){
this.setState({ fetching: true })
api.getMonuments().then(res => {
this.setState({
api: res,
fetching: false
})
})
}
I got this data: an array of 4 objects
Then I want to pass that data to another scene. I'm doing it like this:
<View style={styles.contentContainer}>
<TouchableHighlight
style={[styles.button, {marginBottom:0}]}
onPress={() => navigate('Monumento', this.state.api)}
underlayColor='#000'
>
<View style={styles.buttonContent}>
<Animatable.Text
style={styles.buttonText}
animation="bounceInLeft"
duration={1500}
>
Click here!
</Animatable.Text>
</View>
</TouchableHighlight>
</View>
On the other scene I get that data with the navigation.state.params but the problem now is that there is no more an array with 4 objects in it, but instead there is an object that have 4 objects in it...if I console log the data that is what's appears
render(){
const api = this.props.navigation.state.params;
console.log('API:', api)
...
Now I want to use the map function but I can't because 'api' is not a function...How can I workaround this?
render(){
var api={"bar":"nihao"};
return(
<View>
{Object.entries(api).map(([key,v])=>{
return <View key={key}><Text>{v}</Text></View>
})}
</View>
)
}
api is a single object not array.
api is a array.
render(){
var api=[{"bar":"nihao"},{"bar":"nihao2"},{"bar":"nihao3"}];
return(
<View>
{api.map((v,index)=>{
return <View key={index}><Text>{v.bar}</Text></View>
})}
</View>
)
}
You can use Object.entries with RN for mapping the key/value pairs of an object. Eg:
const api = { 'foo': 'bar', 'foz': 'baz'};
...
render() {
return (
Object.entries(api).map(([key, value]) => {
return <View key={key}>{value}</View>
});
)
}
The issue is you are accessing params object, but what you want is api array. I guess you are using react navigation. If so, then your call to navigate function should be like this:
navigate('Monumento', {api: this.state.api}).
And you can retrieve it like this:
this.props.navigation.state.params.api.
Navigate function takes screen name and params object.
Read this: https://reactnavigation.org/docs/navigators/navigation-prop#navigate-Link-to-other-screens