React-native : show content on two rows - react-native

I want to show 6 options per row instead of showing only one option per row.
I'm still new in react-native
Here is the code :
import React from 'react';
import {fromJS} from 'immutable';
import {ScrollView, StyleSheet, TouchableOpacity} from 'react-native';
import {Text} from 'src/components';
import Option from './OptionVariable';
import {checkOption} from 'src/modules/product/helper';
import {margin} from 'src/components/config/spacing';
class AttributeVariable extends React.Component {
onSelectOption = (option) => {
const {onSelectAttribute, attribute} = this.props;
onSelectAttribute(attribute.get('id'), attribute.get('name'), option.get('option'));
};
render() {
const {attribute, meta_data, variations} = this.props;
// Attribute selected
const attributeSelected = meta_data.find(attr => attr.get('id') === attribute.get('id') && attr.get('name') === attribute.get('name'));
return (
<>
<Text>
{attribute.get('name')}: <Text colorSecondary>{attributeSelected ? attributeSelected.get('option') : ''}</Text>
</Text>
<ScrollView
style={styles.attribute}
vertical
showsHorizontalScrollIndicator={false}>
{attribute.get('options').map(option => {
const disabled = meta_data.size === 0 ? false : !checkOption(variations, meta_data, fromJS({
id: attribute.get('id'),
name: attribute.get('name'),
option: option.get('option'),
}));
return (
<TouchableOpacity
activeOpacity={disabled ? 1 : 0}
key={option}
onPress={() => disabled ? {} : this.onSelectOption(option)}
>
<Option
type={attribute.get('type')}
selected={attributeSelected && option.get('option') === attributeSelected.get('option')}
disabled={disabled}
option={option}
/>
</TouchableOpacity>
)
})}
</ScrollView>
</>
);
}
}
const styles = StyleSheet.create({
attribute: {
marginTop: margin.small,
},
});
export default AttributeVariable;
Here is how it looks :
I hope everything is clear to you.
Sorry for my bad english it's not my main language....
Thanks in advance for any help.

you can say in your attribute style:
attribute: {
marginTop: margin.small,
flexDirection: 'row'
},
so it should align in rows instead of columns
That should help you to learn more about styling components in react native: https://facebook.github.io/react-native/docs/flexbox

Try using the Flatlist (instead of ScrollView) component passing the horizontal={true} props.
Like this:
FlatList
horizontal={true}
data={attribute.option}
renderItem={({ item }) => "Pass your component"}
keyExtractor={item => item.id}
/>

UPDATE 2 :
I used View instead of ScrollView with the following styles and it worked just fine..
<View style={[styles.container, ]}
horizontal
style={{AlignContent: 'flex-start', flexDirection: 'row', alignItems: 'flex-start',}}
style={styles.attribute}
showsHorizontalScrollIndicator={false}>
UPDATE 1 :
I solved the problem using this :
container: {
marginTop: margin.small,
flexDirection: 'row-reverse',
position: 'relative',
width: 'auto',
alignSelf: 'stretch',
maxWidth: 29,
height: 29,
marginBottom: margin.big,
borderRadius: borderRadius.base,
borderWidth: 1,
},
I changed the size of option so all of them appear on the same row (not exactly what I want but it works)
If anyone have a better answer please share.

Related

React Native : onChangeText how to handle two events?

I am making a upload user introduction function ,
the idea is :
the input box of introduction is able to add a characters count ;
and is able to use formik to upload also ..
I have noticed that ,the onChangeText only able to handle one event once ..therefore ,I made a function ,in order to handle two events ...
However ,it dose't work ..
Could you please take a look my code ?
Thank you so much !
import React,{useState} from 'react';
import { StyleSheet,View,Text } from 'react-native';
import ErrorMsg from './ErrorMsg';
import { useFormikContext } from 'formik';
import AppTextInput from './AppTextInput';
import colors from '../config/colors';
function AppFormFliedUserInfo({name,width='90%',number,style,...otherProps}) {
const{setFieldTouched, handleChange, errors, touched }= useFormikContext();
const [value,setValue] = useState('');
function twoCalls(name){
handleChange(name);
setValue(text);
}
return (
<View style={[styles.container,{width:width} ,style]}>
<View style={styles.first_container}>
<ErrorMsg error={errors[name]} visible={touched[name]}/>
<AppTextInput
onBlur = {()=>setFieldTouched(name)}
onChangeText = {twoCalls}
width = {width}
number ={number}
maxLength={number}
{...otherProps}
/>
</View>
{/**Count */}
{number&&(
<View style={styles.count_container}>
<Text>{value === "" ? "0" : value.length}/{number}</Text>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
count_container : {
width : "100%",
alignItems : 'flex-end',
marginTop : 10,
},
container :{
padding: 15,
flexDirection: "column",
marginVertical : 10,
borderRadius: 25,
backgroundColor: colors.light,
},
first_container : {
flexDirection : "row",
flex : 1,
},
})
export default AppFormFliedUserInfo;
It looks like it's just a typo:
function twoCalls(name){
handleChange(name);
setValue(name); //Change "text" to "name"
}

react native flat list how to force list items to be the same height?

I have a React-Native application where I am using FlatList to display a list of items obtained from the server. The list has 2 columns and I need my list items to be the same height. I put a border around the code rendering my list items but the list items are not the same height. I have tried using flexbox settings to make the view fill the container, but everything I try makes no difference.
I have created a simplified version of my app to illustrate the issue:
See that the red bordered areas are NOT the same height. I need to get these to be the same height.
The grey border is added in the view wrapping the component responsible for a list item and the red border is the root view of the component responsible for a list item. See the code below for clarity.
I can not use the grey border in my application because my application shows empty boxes whilst the component responsible for a list item is getting additional information from the server before it renders itself
Furthermore I can not used fixed sizes for heights.
Application Project structure and code
My code is split up in a manner where the files ending in "container.js" get the data from the server and pass it to its matching rendering component. For example, "MainListContainer" would be getting the list from the server and then pass the list data to "MainList", and "ListItemContainer" would get additional information about the single list item from the server and pass it to "ListItem" to render the actual item. I have kept this model in my simplified application so its as close to my real application as possible.
index.js
import {AppRegistry} from 'react-native';
import MainListContainer from './app/components/MainListContainer';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => MainListContainer);
MainListContainer.js
import React from 'react';
import MainList from './MainList';
const data = [
{id: '1', title: 'Item 1', subtitle: 'A', description: 'This is the first item.'},
{id: '2', title: 'Item 2', subtitle: 'B', description: 'The Big Brown Fox Jumped over the lazy dogs. The Big Brown Fox Jumped over the lazy dogs.',},
];
const MainListContainer = () => {
return ( <MainList items={data} /> );
};
export default MainListContainer;
MainList.js
import React from 'react';
import {StyleSheet, FlatList, View} from 'react-native';
import ListItemContainer from './ListItemContainer';
export default class MainList extends React.Component {
constructor(props) {
super(props);
this.state = { numColumns: 2};
this.renderItem = this.renderItem.bind(this);
}
renderItem({item, index}) {
return (
<View style={styles.flatListItemContainer}> <!-- THIS IS WHERE THE GREY BORDER IS ADDED -->
<ListItemContainer key={index} item={item} />
</View>
);
}
render() {
const {items} = this.props;
const {numColumns} = this.state;
return (
<View>
<FlatList
data={items}
renderItem={this.renderItem}
numColumns={numColumns}
key={numColumns}
keyExtractor={(item) => item.id}
/>
</View>
);
}
};
const styles = StyleSheet.create({
flatListItemContainer: {
flex: 1,
margin: 10,
borderColor: '#ccc',
borderWidth: 1,
},
});
ListItemContainer.js
import React from 'react';
import ListItem from './ListItem';
const ListItemContainer = (props) => {
const { item } = props;
return (
<ListItem item={item} />
);
};
export default ListItemContainer;
ListItem.js
import React from 'react';
import {TouchableHighlight, View, StyleSheet, Image, Text} from 'react-native';
const ListItem = (props) => {
const { item } = props;
return (
<TouchableHighlight
underlayColor="white"
>
<View style={styles.containerView}> <!-- THIS IS WHERE THE RED BORDER IS ADDED -->
<View style={styles.top_row}>
<Image style={styles.image} source={require('../images/placeholder.png')} />
<View style={styles.title_texts}>
<Text style={{fontWeight:'bold'}}>{item.title}</Text>
<Text style={{color: 'rgb(115, 115, 115)'}}>{item.subtitle}</Text>
</View>
</View>
<Text>{item.description}</Text>
</View>
</TouchableHighlight>
);
};
export default ListItem;
const styles = StyleSheet.create({
containerView: {
padding: 14,
borderColor: 'red',
borderWidth: 1,
},
top_row: {
flex: 1,
flexDirection: 'row',
marginBottom: 10,
},
title_texts: {
flex: 1,
flexDirection: 'column',
},
image: {
alignSelf: 'flex-end',
resizeMode: 'cover',
height: 40,
width: 40,
marginRight: 20
},
});
What I have tried
ListItem.js : move the style onto the "TouchableHighlight" view
ListItem.js : add a view wrapping "TouchableHighlight" view and adding style there
ListItem.js : added "alignItems:'stretch' on the "TouchableHighlight, added it to the "containerView" style, tried it on the description field too
same as "alignItems" but used "alignedSelf" instead
same as "alignItems" but used "alignedContent" instead
tried using "flexGrow" on different views (container, description)
You can measure the height of every element in the list and when you determine the maximum height, you can use that height for every element in the list.
const Parent = ({ ...props }) => {
const [maxHeight, setMaxHeight] = useState<number>(0);
const computeMaxHeight = (h: number) => {
if (h > maxHeight) setMaxHeight(h);
}
return (
<FlatList
data={props.data}
renderItem={({ item }) => (
<RenderItem
item={item}
computeHeight={(h) => computeMaxHeight(h)}
height={maxHeight}
/>
)}
....
/>
)
}
The Items:
const RenderItem = ({...props }) => {
return (
<View
style={{ height: props.height }}
onLayout={(event) => props.computeHeight(event.nativeEvent.layout.height)}
>
<Stuffs />
</View>
)
}
This is a very non-performant way of achieving this. I would avoid this if I have a long list or any list of more than a few items. You however can put certain checks in place to limit rerendering etc. Or alternatively if it is only text that will affect the height, then you can only measure the height of the element with the most text and use that element's height for the rest.
Instead of set fixed width height, you can use flex box to achieve it. I just solved the issue by removing alignSelf at the FlatList and add alignItems center on it.
Wrap the flatList in flex box with align item center, you can add the code in your MainList.js file, the first <View>, i.e:
render() {
const {items} = this.props;
const {numColumns} = this.state;
return (
<View style={{flex: 1, alignItems: 'center'>
<FlatList
data={items}
renderItem={this.renderItem}
numColumns={numColumns}
key={numColumns}
keyExtractor={(item) => item.id}
/>
</View>
);
If still not reflected, you may try to add flex:1, alignItems center in FlatList style props.
You are missing a very basic concept of giving fixed height to the flatlist items, in your ListItem.js, try to set height:200 in containerView. Let me know if that works for you

How to implement modal swipe to down like instagram?

I'm currently working on an React Native project. I need to implement a feature similar to "Swipe Down To close Modal" on Instagram
I believe that you're using #react-native-community/react-native-modal package. In the library documents, you can see that there is a property called swipeDirection which is either a string or an array that can be one or many of the following options; 'up', 'down', 'left' or 'right.
You can also set the threshold required swipe action to be completed by using swipeThreshold property. The default is 100 according to the library documents.
Here's an example of the modal;
import React from 'react';
import {View, Text} from 'react-native;
import Modal from 'react-native-modal';
const ModalComponent = props => {
const [isVisible, setIsVisible] = useState(true);
return (
<Modal isVisible={isVisible}
swipeDirection="down"
//swipeDirection={["up", "down", "left", "right"]}
onSwipeComplete={(e) => { setIsVisible(false); })
style={{ justifyContent: 'flex-end', margin: 0, }} >
<View style={{ backgroundColor: 'steelblue' }}>
<Text>Hello Modal</Text>
</View>
</Modal>
);
};
export {ModalComponent};

React-native FlatList item changes position after update

I have created a flatlist and each item has a button that onPress will change item value. The issue comes up when I change item value, as that item will also unexpectedly fall down to the last position of the flatlist.
This is happening only from React-Native version 57 +
The 55.4 version didnt' run into this problem, but I need to upgrade react-native to 57+.
Thanks in advance for any help :)
Here is the sample code:
import React, { Component } from 'react';
import { View, Text, FlatList, StyleSheet, Button } from 'react-native';
class List extends Component {
state = {
currencies: {
USD: 0,
EUR: 0,
GBP: 0,
BTC: 0,
CNY: 0,
AUD: 0,
JPY: 0
}
}
render() {
return (
<View style={styles.container}>
<FlatList
extraData={this.state}
data={Object.keys(this.state.currencies)}
style={{ flex: 1 }}
renderItem={({ item }) => (
<Row
itemValue={this.state.currencies[item]}
itemName={item}
numberUp={(name) => this.setState({currencies:
{...this.state.currencies,
[name] : this.state.currencies[name] + 1}
})
}
/>
)}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
},
itemContainer : {
flexDirection: "row",
justifyContent: "center",
margin: 10
},
itemText : {
fontSize: 30
}
});
export default List;
const Row = (props) => (
<View style={styles.itemContainer}>
<Text style={styles.itemText}>{props.itemName} </Text>
<Text style={styles.itemText}>{props.itemValue} </Text>
<Button title="PLUS" onPress={() => props.numberUp(props.itemName)}/>
</View>
);
you are trying to append item into the list. that's why it is displaying item at the end of the list.
try below code, I have tried it and it's working:
render() {
return (
<View style={styles.container}>
<FlatList
extraData={this.state}
data={Object.keys(this.state.currencies)}
style={{ flex: 1 }}
renderItem={({ item }) => (
<Row
itemValue={this.state.currencies[item]}
itemName={item}
numberUp={(name) => {
let currencyList = this.state.currencies;
currencyList[name] = this.state.currencies[name] + 1;
this.setState({currencies: currencyList})
}
}
/>
)}
/>
</View>
)
}
Object doesn't maintain the property order. Thats why order gets changed in your list when you update it. ECMA script defines object as
An object is a member of the type Object. It is an unordered
collection of properties each of which contains a primitive value,
object, or function. A function stored in a property of an object is
called a method.
So to maintain the order its better to use an array instead.
currencies: [
{currency:USD, value:0},
{currency:EUR, value:0},
{currency:GBP, value:0},
{currency:BTC, value:0},
{currency:CNY, value:0},
{currency:JPY, value:0},
]

setNativeProps Change Value for Text Component React Native Direct Manipulation

I want to directly update the value of a component due to performance reasons.
render(){
<View>
<Text style={styles.welcome} ref={component => this._text = component}>
Some Text
</Text>
<TouchableHighlight underlayColor='#88D4F5'
style={styles.button}>
<View>
<Text style={styles.buttonText}
onPress={this.useNativePropsToUpdate.bind(this)}>
Iam the Child
</Text>
</View>
</TouchableHighlight>
</View>
}
This is the method I use to update the text component. I dont know if I am setting the right attribute/ how to figure out which attribute to set:
useNativePropsToUpdate(){
this._text.setNativeProps({text: 'Updated using native props'});
}
Essentially trying to follow the same approach from this example:
https://rnplay.org/plays/pOI9bA
Edit:
When I attempt to explicitly assign the updated value:
this._text.props.children = "updated";
( I know this this the proper way of doing things in RN ). I get the error "Cannot assign to read only property 'children' of object'#'"
So maybe this is why it cant be updated in RN for some reason ?
Instead of attempting to change the content of <Text> component. I just replaced with <TextInput editable={false} defaultValue={this.state.initValue} /> and kept the rest of the code the same. If anyone know how you can change the value of <Text> using setNativeProps OR other method of direct manipulations. Post the answer and ill review and accept.
The text tag doesn't have a text prop, so
this._text.setNativeProps({ text: 'XXXX' })
doesn't work.
But the text tag has a style prop, so
this._text.setNativeProps({ style: { color: 'red' } })
works.
We can't use setNativeProps on the Text component, instead, we can workaround and achieve the same result by using TextInput in place of Text.
By putting pointerEvent='none' on the enclosing View we are disabling click and hence we can't edit the TextInput (You can also set editable={false} in TextInput to disbale editing)
Demo - Timer (Count changes after every 1 second)
import React, {Component} from 'react';
import {TextInput, StyleSheet, View} from 'react-native';
class Demo extends Component {
componentDidMount() {
let count = 0;
setInterval(() => {
count++;
if (this.ref) {
this.ref.setNativeProps({text: count.toString()});
}
}, 1000);
}
render() {
return (
<View style={styles.container} pointerEvents={'none'}>
<TextInput
ref={ref => (this.ref = ref)}
defaultValue={'0'}
// editable={false}
style={styles.textInput}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 0.7,
justifyContent: 'center',
alignItems: 'center',
},
textInput: {
fontSize: 60,
width: '50%',
borderColor: 'grey',
borderWidth: 1,
aspectRatio: 1,
borderRadius: 8,
padding: 5,
textAlign: 'center',
},
});
export default Demo;
As setNativeProps not solving the purpose to alter the content of <Text />, I have used below approach and is working good. Create Simple React Component like below...
var Txt = React.createClass({
getInitialState:function(){
return {text:this.props.children};
},setText:function(txt){
this.setState({text:txt});
}
,
render:function(){
return <Text {...this.props}>{this.state.text}</Text>
}
});