React Native component loose arrangement when a new component is added - react-native

I have the following view in a React Native app:
And this is the code for it:
index.js
import React, {useContext} from 'react';
import {View, StyleSheet} from 'react-native';
import {Headline, Button, IconButton} from 'react-native-paper';
import ShoppingCartEntry from './ShoppingCartEntry';
import ShoppingCartContext from '../../context/shoppingCart/shoppingCartContext';
const ShoppingCartList = () => {
const {shoppingCart} = useContext(ShoppingCartContext);
const TotalPriceDisplay = () => {
let totalPrice = 0;
shoppingCart.map((entry) => {
totalPrice = totalPrice + (entry.product.price * entry.amount);
})
return (
<Headline>Total: {totalPrice}</Headline>
)
}
return (
<View style={styles.cartContainer}>
<View style={styles.icon}>
<IconButton icon='cart' color='black' size={40}/>
</View>
{shoppingCart.map((entry) => {
return (
<ShoppingCartEntry entry={entry} />
)
})}
<View style={styles.totalPrice}>
<TotalPriceDisplay />
<Button mode='contained'>
Comprar
</Button>
</View>
</View>
)
}
styles = StyleSheet.create({
cartContainer: {
flexDirection: 'column',
},
totalPrice: {
alignItems: 'center'
},
icon: {
alignItems: 'center'
}
})
export default ShoppingCartList;
ShoppingCartEntry.js
import React, {useContext} from 'react';
import {View, StyleSheet} from 'react-native';
import {Headline, IconButton, Text} from 'react-native-paper';
import ShoppingCartContext from '../../context/shoppingCart/shoppingCartContext';
const ShoppingCartEntry = ({entry}) => {
const {shoppingCartAppendProduct, shoppingCartUpdateProduct, shoppingCartRemoveProduct} = useContext(ShoppingCartContext);
const removeProduct = () => {
shoppingCartRemoveProduct(entry.product.id);
}
const Counter = () => {
return (
<View style={styles.counterContainer}>
<View style={styles.counter}>
<IconButton icon='plus' color='black' onPress={removeProduct} />
</View>
<View>
<Headline color='blue'>{entry.amount}</Headline>
</View>
<View style={styles.counter}>
<IconButton icon='minus' color='black' onPress={removeProduct} />
</View>
</View>
)
}
return (
<View style={styles.entryContainer}>
<View style={styles.button}>
<IconButton icon='delete' color='red' onPress={removeProduct} />
</View>
<View style={styles.name}>
<Headline>{entry.product.name}</Headline>
<Text>{entry.product.price}</Text>
</View>
<View style={styles.amount}>
<Counter />
<Text style={{textAlign: 'right'}}>{entry.product.price * entry.amount}</Text>
</View>
</View>
)
}
styles = StyleSheet.create({
entryContainer: {
flexDirection: 'row',
paddingBottom: 20,
paddingLeft: 20
},
name: {
flex: 0.5
},
amount: {
flex: 0.3
},
button: {
alignItems: 'flex-end',
flex: 0.1
},
counterContainer: {
flexDirection: 'row'
},
counter: {
alignItems: 'flex-end',
justifyContent: 'center',
flexDirection: 'row'
}
})
export default ShoppingCartEntry;
The problem is that when an element is removed from shoppingCart and the whole view is re rendered, it looses all its formatting:
I discovered that all I have to do is to change the StyleSheet.flexDirection for the container from row to column and, then, back to row again, and the view gets arranged again, but now with the new data.
I don't know if this is a development stage issue that I shouldn't care about for production, or if there is something I can do to avoid it.

You should add flex:1 to your base container styles for both your list and your item.
...
entryContainer: {
flex:1,
flexDirection: 'row',
paddingBottom: 20,
paddingLeft: 20
},
...
cartContainer: {
flex:1,
flexDirection: 'column',
},

Related

React-native I loop through it, but nothing comes out of the children

I tried to show images and people's names using loops.
import { StyleSheet, Text, View, SafeAreaView, ScrollView } from 'react-native'
import React from 'react'
import Header from './home/Header'
import Stories from './home/Stories'
import Posts from './home/Posts'
import { POSTS } from '../data/posts'
const HomeScreen = () => {
return (
<SafeAreaView style={styles.container}>
<Header></Header>
<ScrollView>
<Stories></Stories>
{POSTS.map((post, index) => (
<Posts post={post} key={index}/>
))}
</ScrollView>
</SafeAreaView>
)
}
export default HomeScreen
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'black'
},
});
This is the one that has a problem.
import { Image, StyleSheet, Text, View } from 'react-native'
import {Divider} from 'react-native-elements'
import React from 'react'
const Posts = ({post, index}) => {
return (
<View stlye={styles.conatiner}>
<Divider width={1} orientation='vertical'></Divider>
<PostHeader post={post} key={index}/> <--- this part not show up
</View>
)
};
const PostHeader = ({post, index}) => {
<View style={{flexDirection: 'row', justifyContent: 'space-between', margin: 5, alignContent: 'center'}} key={index}>
<View>
<Image source={{ uri: post.profile_pictrue}} style={styles.story}/>
<Text style={{color: 'white'}}>{post.user.user}</Text>
</View>
</View>
};
const styles = StyleSheet.create({
conatiner: {
marginBottom: 30
},
story: {
width: 35,
height: 35,
borderRadius: 50,
marginBottom: 10,
borderWidth: 2,
borderColor: 'orange'
},
});
export default Posts
But the divider is working. And, there is no error log.
I don't understand what have I done wrong.
you need to wrap with return
const PostHeader = ({post, index}) => {
return( <View style={{flexDirection: 'row', justifyContent: 'space-between', margin: 5, alignContent: 'center'}} key={index}>
<View>
<Image source={{ uri: post.profile_pictrue}} style={styles.story}/>
<Text style={{color: 'white'}}>{post.user.user}</Text>
</View>
</View>)
};
hope it's working fine

React native button onpress show 2 new buttons and make other stuff unclickable

I want after press on the FloatingButton, to show 2 more buttons.
While I have these 2 buttons shown, I want to make the rest of this page to be uncklickable,
that the user has to press one of these to go on navigating.
This is my code:
import React from 'react';
import { StyleSheet, Text, View, ScrollView } from 'react-native';
import FloatingButton from '../components/FloatingButton';
const DuellScreen = () => {
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.container2}>
<Text style={{ flex: 1 }}>body</Text>
</ScrollView>
<View style={styles.container}>
<FloatingButton/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
padding: 16,
flex: 1,
backgroundColor: '#fff',
justifyContent: 'flex-end',
alignItems: 'center'
},
container2: {
padding: 16,
flex: 5,
backgroundColor: '#fff',
},
});
export default DuellScreen
How can I do this, especially the 2 button visible after buttonpress + make the other things unclickable?
how about this:
import React, { useState } from 'react';
import { StyleSheet, Text, View, ScrollView } from 'react-native';
import FloatingButton from '../components/FloatingButton';
const DuellScreen = (props) => {
const [active, setActive] = useState(true);
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.container2}>
<Text style={{ flex: 1 }}>body</Text>
</ScrollView>
<View style={styles.container}>
<FloatingButton onPress={active ? () => setActive(false) : () => {} } />
{!active ?
<View>
<FloatingButton onPress={() => props.navigation.navigate('OtherScreen')} />
<FloatingButton onPress={() => props.navigation.navigate('AnotherScreen')} />
</View>
: null}
</View>
</View>
)
}

How to get a param of an id using react-navigation v5?

I'm trying to update an app I've developed using react-navigation v4.
Using react-navigation v4 I could get the id using something like console.log(navigation.getParam('id'));
But when I tried the same after updating to v5 of react-navigation it shows me an error and I can't find the right way to get the param id
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function ShowScreen({ navigation }) {
console.log(navigation.getParam('id'));
return (
<View style={styles.container}>
<Text>Show Screen</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
The other screen where the id is located is it:
import React, { useContext } from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import { FlatList, TouchableOpacity } from 'react-native-gesture-handler';
import { Context } from '../../context/BlogContext';
import Icon from 'react-native-vector-icons/Feather'
export default function IndexScreen({ navigation }) {
const { state, addBlogPost, deleteBlogPost } = useContext(Context);
return (
<View style={styles.container}>
<Button
title="Add Post"
onPress={addBlogPost}
/>
<FlatList
data={state}
keyExtractor={(blogPost) => blogPost.title}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={
() => navigation.navigate('ShowScreen',
{ id: item.id })}>
<View style={styles.row}>
<Text style={styles.title}>
{item.title} -
{item.id}
</Text>
<TouchableOpacity onPress={() => deleteBlogPost(item.id)}>
<Icon style={styles.icon}
name="trash"
/>
</TouchableOpacity>
</View>
</TouchableOpacity>
);
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 30,
justifyContent: 'center',
alignItems: 'center'
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 10,
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#333'
},
title: {
fontSize: 18
},
icon: {
fontSize: 24
}
});
You can get params using route.params in react navigation v5 more on that read here
just update your code to this
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function ShowScreen({ navigation,route }) {
const {id} =route.params; //you will get id here via route
return (
<View style={styles.container}>
<Text>Show Screen</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
you can simply access your parameters like this
const id = this.props.route.params.id
inside this.props.route.params you will get all paramaters :)

A problem with a trivial react native code with hooks

Update:
Problem 2. is solved (thanks to #NikolayNovikov reply) by replacing "setResources({ resources: response.data })" with
"setResources(response.data)".
Problem 1. I cannot reproduce it...
Description of the problem:
The following trivial example is fetching data from jsonplaceholder.typicode.com, and, since there are 100 'posts' and 200 'todos', it is supposed to display 100 and 200 when you click on these buttons.
For some reason that I cannot find (embarrassing, I know...), there are two problems:
When I click on the 'posts' button, useEffect is called twice (once with 'todos' and once with 'posts', and when I click on the 'todos' button it is not called at all.
resource.length is not rendered
Any idea what is wrong?
App.js:
import React, { useState } from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { ResourceList } from './components/ResourceList';
console.warn('In App.js: Disabling yellowbox warning in genymotion');
console.disableYellowBox = true;
export const App = () => {
const [resource, setResource] = useState('todos');
return (
<View style={{ flex: 1, alignItems: 'center', marginTop: 100 }}>
<Text style={{ fontSize: 20, fontWeight: '500' }}>
The Application has been loaded!
</Text>
<View style={{ flexDirection: 'row', marginTop: 100,
alignItems: 'center', justifyContent: 'center' }}>
<TouchableOpacity onPress={() => setResource('posts')}>
<View style={styles.button}>
<Text style={styles.buttonText}>
Posts
</Text>
</View>
</TouchableOpacity>
<View style={{ width: 20 }} />
<TouchableOpacity onPress={() => setResource('todos')}>
<View style={styles.button}>
<Text style={styles.buttonText}>
Todos
</Text>
</View>
</TouchableOpacity>
</View>
<ResourceList resource={resource} />
</View>
);
}
const styles = StyleSheet.create({
buttonText: {
color: 'black',
fontSize: 20
},
button: {
backgroundColor: '#a8a',
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 5,
paddingHorizontal: 10,
borderRadius: 2
}
});
ResourceList.js:
import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import axios from 'axios';
// export const ResourceList = (props) => {
export const ResourceList = ({ resource }) => { // restructured props
const [resources, setResources ] = useState([]);
const fetchResource = async(resource) => {
console.log('+++ ResourceList/fetchResource calling axios. path:',
`https://jsonplaceholder.typicode.com/${resource}`);
const response = await axios.get(`https://jsonplaceholder.typicode.com/${resource}`);
console.log(response);
setResources({ resources: response.data })
}
useEffect(() => {
console.log('--- ResourceList/useEffect. resource: ', resource);
fetchResource(resource);
}, [resource]);
return (
<View style={{ flex: 1, alignItems: 'center', marginTop: 100 }}>
<Text style={{ fontSize: 20, fontWeight: '500' }}>
{resources.length}
</Text>
</View>
);
}
In you ResourceList.js component setResources should be different, please try this:
setResources(response.data)

Creation of a new component with navigation fields

I try to create a new react-native component that contains two button. I define two property next to going on the next page and prev to going in the precedent page but I have this error:
undefined is not an object (evaluating '_this.props.navigation.navigate')
I found so much question similar this but I don't found any valid solution.
This is the code:
import PropTypes from 'prop-types'
import React, { Component } from 'react';
import { View, StyleSheet, Button } from 'react-native';
export default class ButtonNavigation extends Component {
static propTypes = {
next: PropTypes.string,
prev: PropTypes.string,
}
render() {
const { next, prev } = this.props;
return (
<View style={styles.bottomContainer}>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.navigation.navigate(next)} title='Indietro' color='#00b0ff'/>
</View>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.navigation.navigate(prev)} title='Avanti' color='gold'/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
bottomContainer: {
flex: 1,
flexDirection: 'row',
position: 'absolute',
bottom: 10,
padding: 20,
justifyContent: 'space-between'
},
text: {
fontSize: 30,
},
buttonContainer: {
flex: 1,
padding: 20,
justifyContent: 'center',
alignContent: 'center',
}
});
I include the object in the other page in this way:
<ButtonNavigation next={'NumberVehicle'} prev={'Home'}/>
In your <ButtonNavigation next={'NumberVehicle'} prev={'Home'}/> you need to add the navigation object as a prop. So something like:
<ButtonNavigation
next={'NumberVehicle'}
prev={'Home'}
navigation={this.props.navigation}
/>
(Make sure to replace this.props.navigation if it differs from the actual navigation object)
And after you can do:
const { next, prev, navigation } = this.props;
and
navigation.navigate(..)
You can change this
<ButtonNavigation next={this.props.navigation.navigate('NumberVehicle')} prev={this.props.navigation.navigate('Home')}/>
<View style={styles.bottomContainer}>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.next} title='Indietro' color='#00b0ff'/>
</View>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.prev} title='Avanti' color='gold'/>
</View>
</View>