Multi select dropdown in react native - reactjs

I am new to react native. Can anyone suggest how do i implement multiple select dropdown in react native. I have tried MultiSelect (https://github.com/toystars/react-native-multiple-select) from react-native-multiple-select but it is not working.

This is a source code of implemented multiselect source list
import React from 'react';
import {View, Text, StyleSheet, FlatList, TouchableHighlight, Dimensions} from 'react-native';
var thisObj;
var deviceHeight = Dimensions.get("window").height;
class MyListItem extends React.PureComponent {
render() {
return (
<View style={{flex: 1}}>
<TouchableHighlight onPress={this.props.onPress.bind(this)} underlayColor='#616161'>
<Text style={this.props.style}>{this.props.item.key}</Text>
</TouchableHighlight>
</View>
);
}
}
export default class MultiSelect extends React.Component {
constructor(props) {
super(props);
var selectedItemsObj = {};
if(this.props.selectedItems) {
var items = this.props.selectedItems.split(',');
items.forEach(function(item) {
selectedItemsObj[item] = true;
});
}
this.state = {
selectedItems: selectedItemsObj
};
}
onItemPressed(item) {
var oldSelectedItems = this.state.selectedItems;
var itemState = oldSelectedItems[item.key];
if(!itemState) {
oldSelectedItems[item.key] = true;
}
else {
var newState = itemState? false: true;
oldSelectedItems[item.key] = newState;
}
this.setState({
selectedItems: oldSelectedItems,
});
var arrayOfSelectedItems = [];
var joinedItems = Object.keys(oldSelectedItems);
joinedItems.forEach(function(key) {
if(oldSelectedItems[key])
arrayOfSelectedItems.push(key);
});
var selectedItem = null;
if(arrayOfSelectedItems.length > 0)
selectedItem = arrayOfSelectedItems.join();
this.props.onValueChange(selectedItem);
}
componentWillMount() {
thisObj = this;
}
getStyle(item) {
try {
console.log(thisObj.state.selectedItems[item.key]);
return thisObj.state.selectedItems[item.key]? styles.itemTextSelected : styles.itemText;
} catch(e) {
return styles.itemText;
}
}
_renderItem = ({item}) => {
return (<MyListItem style={this.getStyle(item)} onPress={this.onItemPressed.bind(this, item)} item={item} />);
}
render() {
return (
<View style={styles.rootView}>
<FlatList style={styles.list}
initialNumToRender={10}
extraData={this.state}
data={this.props.data}
renderItem={this._renderItem.bind(this)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
rootView : {
height: deviceHeight / 2
},
itemText: {
padding: 8,
color: "#fff"
},
itemTextSelected: {
padding: 8,
color: "#fff",
backgroundColor: '#757575'
},
list: {
flex: 1,
}
});
This is how the component should be used
this.state = {
selectedItem : null,
data: [{key:"key1", label:"label1"}, {key:"key2", label:"label2"}]
}
...
<MultiSelect
data={this.state.data}
selectedItems={this.state.selectedItem}
onValueChange={ (itemValue) => thisObj.setState({selectedItem: itemValue})}/>
Selected values will be joined and set in this.state.selectedItem field

I have implemented React Native component.
Source code is attached.
It shows how to make list checkable.
It may be a base for your solution.
Please see.
import React from 'react';
import {View, Text, StyleSheet, FlatList, TouchableHighlight} from 'react-native';
var thisObj;
export default class MultiSelect extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItems: {}
};
}
onItemPressed(item) {
var oldSelectedItems = this.state.selectedItems;
var itemState = oldSelectedItems[item.key];
if(!itemState) {
oldSelectedItems[item.key] = true;
}
else {
var newState = itemState? false: true;
oldSelectedItems[item.key] = newState;
}
this.setState({
selectedItems: oldSelectedItems,
});
}
componentDidMount() {
thisObj = this;
}
getStyle(item) {
try {
console.log(thisObj.state.selectedItems[item.key]);
return thisObj.state.selectedItems[item.key]? styles.itemTextSelected : styles.itemText;
} catch(e) {
return styles.itemText;
}
}
render() {
return (
<View style={styles.rootView}>
<FlatList style={styles.list}
extraData={this.state}
data={this.props.data}
renderItem={({item}) =>
<TouchableHighlight onPress={this.onItemPressed.bind(this, item)} underlayColor='#f00'>
<Text style={this.getStyle(item)}>{item.key}</Text>
</TouchableHighlight>
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
rootView : {
},
itemText: {
padding: 16,
color: "#fff"
},
itemTextSelected: {
padding: 16,
color: "#fff",
backgroundColor: '#f00'
},
list: {
}
});
How to use this
<Multiselect data = { [{"key" : "item1"}, {"key" : "item2"}{"key" : "item3"}]
}\>

Related

set Multiple state Id for custom component in React Native

I have implemented custom inputBox component. So When I am using this component first time then it is working fine and when I am using multiple time in one page then data is prepopulate to next component.
Custom component:
import React, { createRef } from 'react';
import {
View,
TextInput,
Alert,
Text,
StyleSheet
} from "react-native";
class BoxInput extends React.Component {
constructor(props) {
super(props)
this.state = {
digit1: '',
digit2: '',
digit3: '',
...props
}
this.digit1Ref = createRef()
this.digit2Ref = createRef()
this.digit3Ref = createRef()
}
componentDidMount() {
this.digit1Ref.current.focus()
}
componentDidUpdate(prevProps) {
if (this.state.digit1 && this.state.digit2 &&
this.state.digit3
}
saveText(text, key) {
this.setState({ ...this.state, [key]: text }, () => {
if (text) {
key == 'digit1' ? this.digit2Ref.current.focus() : null
key == 'digit2' ? this.digit3Ref.current.focus() : null
key == 'digit3'
}
const boxInputValue = this.state.digit1 + this.state.digit2 + this.state.digit3
this.props.onBoxInput(boxInputValue)
});
}
render() {
return (
<>
<TextInput maxLength={1} keyboardType={'numeric'} ref={this.digit1Ref} style={styles.boxStyle} value={this.state.digit1} onChangeText={(text) => this.saveText(text, 'digit1')} />
<TextInput maxLength={1} keyboardType={'numeric'} ref={this.digit2Ref} style={styles.boxStyle} value={this.state.digit2} onChangeText={(text) => this.saveText(text, 'digit2')} />
<TextInput maxLength={1} keyboardType={'numeric'} ref={this.digit3Ref} style={styles.boxStyle} value={this.state.digit3} onChangeText={(text) => this.saveText(text, 'digit3')} />
</>
)
}
}
const styles = StyleSheet.create({
boxStyle: {
marginTop: 20,
height: 57,
width: 50,
borderRadius: 10,
borderWidth: 1,
borderColor: '#F1F5F9',
backgroundColor: '#F1F5F9',
fontSize: 20,
lineHeight: 40,
paddingHorizontal: 15,
textAlign: 'center'
}
})
export default BoxInput;
import React, { createRef } from 'react';
import styles from './style';
import {
View,
TextInput,
Alert
} from "react-native";
import { connect } from "react-redux";
import * as Animatable from 'react-native-animatable';
import BoxInput from "../../../../md-components/atoms/boxinput"
class MPINScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
confirmMpinEnable: true,
...props
}
this.codes = [{
value: '+91',
}]
}
componentDidUpdate(prevProps) {
if (this.state.mpinValue.split("").length == 3 &&
prevProps.success_msg != this.props.success_msg && this.props.success_msg == 'verified') {
NavigationService.navigate(this.props.navigation, 'MPINVerifyOnboarding')
}
}
handleSubmit = () => {
if (this.state.mpinValue != this.state.confirmMpinValue) {
Alert.alert(
"Error",
"MPIN is not machted",
[
{ text: "OK" }
],
{ cancelable: false }
);
} else {
this.props.verifyMpin({
"mpin": this.state.mpinValue,
phoneNumber: this.props.mobileNumber
})
}
}
mpinConfirmation = () => {
if (this.state.mpinValue.split("").length != 6) {
Alert.alert(
"Error",
"Please insert 6 digit mpin",
[
{ text: "OK" }
],
{ cancelable: false }
);
}else{
this.setState({
confirmMpinEnable: false,
});
}
}
mpinText = (args) => {
this.setState({
mpinValue: args,
});
}
confirmMpinText = (args) => {
this.setState({
confirmMpinValue: args,
});
}
render() {
return (
<>
<HeaderComponent backgroundColor="#E5E5E5" showLeftIcon={true} showRightIcon={false} />
<View style={styles.container}>
<Text style={[styles.textInfo, styles.textTitle]}>We are almost there!</Text>
<View style={styles.imageWrapper}>
<Animatable.View animation="slideInDown" iterationCount={1} style={styles.centerIconWrap}>
<Image style={styles.centerIcon} source={mpin_card} />
</Animatable.View>
</View>
{this.state.confirmMpinEnable ?
<Text style={[styles.textInfo]}>Setup your MPIN</Text> : <Text style={[styles.textInfo]}>Confirm your MPIN</Text>
}
{this.state.confirmMpinEnable ?
<View style={styles.rowWrap}>
<BoxInput id="catFood1" onBoxInput={this.mpinText} />
</View>:
<View style={styles.rowWrap}>
<BoxInput id="catFood2" onBoxInput={this.confirmMpinText} />
</View>
}
<View style={styles.marginBottom}>
<Text style={[styles.mpinNote]}>M-PIN is a short 6-digit PIN that you have to set for</Text>
<Text style={[styles.mpinNote]}>our mandatory Two-Factor Authentication</Text>
</View>
<View style={styles.bottomBtnSyle}>
<View style={styles.multipleBtnStyle}>
<Button onPress={this.handleBack}>{"Back"}</Button>
</View>
{this.state.confirmMpinEnable ?
<View style={styles.multipleBtnStyle}>
<Button onPress={this.mpinConfirmation} >{"Confirm"}</Button>
</View> :
<View style={styles.multipleBtnStyle}>
<Button onPress={this.handleSubmit} >{"Save & Continue"}</Button>
</View>
}
</View>
</View>
</>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MPINScreen);
when I am click on confirm button then hide and display . But in second component data is prepopulating which i was inserted.
in this screen shot data is prepopulate but i want this empty, Because user has to insert again. but it is taking same value from previous state. how we can use multiple time same component in one page.
General idea:
Create a property in MPINScreen state that is changing (incrementing) every attempt (you can call it attempt) and pass it as prop to BoxInput.
In BoxInput create a reset function (that will clean the values of the text inputs and focus the first input). On componentDidUpdate check if attempt prop changed. If true - save the new value in BoxInput state and call "reset".

Retrieve Prop Values from Imported Custom Component

So I have a custom component (TextButton) and I have packaged it inside another component (ContainedButton). I'm currently attempting to style ContainedButton. However, I want to access the value of a prop of TextButton (theme) and use the value when styling ContainedButton.
ContainedButton:
import React, { Component } from 'react';
import TextButton from './TextButton';
class ContainedButton extends Component {
render() {
const { style } = this.props;
return (
<TextButton {...this.props} style={[styles.containedButtonStyle, style]} />
);
}
}
const styles = {
containedButtonStyle: {
backgroundColor: (TextButton prop theme)
padding: 2,
borderWidth: 1,
borderRadius: 5
}
};
export default ContainedButton;
In the parentheses next to 'backgroundColor', I want to insert the value of the theme prop located in TextButton. How would I achieve something like this?
TextButton (in case it's needed):
import React, { Component } from 'react';
import { Text, TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';
class TextButton extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillMount() {}
componentWillReceiveProps(newProps) {
if (newProps.theme !== this.props.theme) {
this.determineTheme(newProps.theme);
}
if (newProps.size !== this.props.size) {
this.determineSize(newProps.size);
}
}
// set the theme
determineTheme = function (theme) {
if (theme === 'primary') {
return {
color: '#0098EE'
};
} else if (theme === 'secondary') {
return {
color: '#E70050'
};
} else if (theme === 'default') {
return {
color: '#E0E0E0'
};
}
return {
color: '#E0E0E0'
};
}
// set the size
determineSize = function (size) {
if (size === 'small') {
return {
fontSize: 16
};
} else if (size === 'medium') {
return {
fontSize: 22
};
} else if (size === 'large') {
return {
fontSize: 28
};
}
return {
fontSize: 22
};
}
render() {
const { onPress, children, theme, size, style } = this.props;
return (
<TouchableOpacity onPress={onPress} style={style}>
<Text style={[this.determineTheme(theme), this.determineSize(size)]}>{children}</Text>
</TouchableOpacity>
);
}
}
TextButton.propTypes = {
onPress: PropTypes.func,
title: PropTypes.string,
theme: PropTypes.string,
size: PropTypes.string
};
export default TextButton;
You can get the value of theme in the same way you get the value of style:
const { theme } = this.props;
Or combine them into a single statement:
const { style, theme } = this.props;

How to make a React Native animation happen again?

Currently, my React Native animation only happens one time then never again. I need it to happen every time one of my props for that component changes. I have the data display changing when the new prop data comes in but it only animates the first time. Is there a way for me to for the animation to happen again every time that props changes/the component updates?
Here is what I have so far:
import React from 'react';
import {Animated, Easing, StyleSheet, Text, View} from 'react-native';
//Animation
class FadeInView extends React.Component {
state = {
yAnimation: new Animated.Value(21),
}
componentDidMount() {
Animated.timing(
this.state.yAnimation,
{
//easing: Easing.bounce,
toValue: 0,
duration: 150,
}
).start();
}
render() {
let { yAnimation } = this.state;
return (
<Animated.View
style={{
...this.props.style,
transform: [{translateY: this.state.yAnimation}],
}}
>
{this.props.children}
</Animated.View>
);
}
}
//Price Component
export default class Price extends React.Component {
constructor(props) {
super(props);
this.animateNow = false;
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.price !== nextProps.price) {
console.log('true');
return true;
} else {
return false;
}
}
componentWillUpdate() {
if (this.props.price != this.localPrice) {
this.animateNow = true;
}
}
componentDidUpdate() {
this.localPrice = this.props.price;
this.animateNow = false;
console.log(this.props.price);
}
render() {
if (this.animateNow) {
return (
<FadeInView>
<Text style={styles.price}>{this.props.price}</Text>
</FadeInView>
);
} else {
return (
<View>
<Text style={styles.price}>{this.props.price}</Text>
</View>
);
}
}
}
const styles = StyleSheet.create({
price: {
fontFamily: 'Avenir',
fontSize: 21,
color: '#606060',
textAlign: 'right',
marginRight: 20,
backgroundColor: 'transparent'
}
});
If you want to animate again when receive props, you should call that again inside componentWillReceiveProps():
playAnimation() {
Animated.timing(
this.state.yAnimation,
{
toValue: 0,
duration: 150,
}
).start();
}
componentWillReceiveProps(next) {
if (next.props.changed) {
this.playAnimation();
}
}

React Native List View Not Changing Style on State Change

My aim is to toggle a switch component and for that change in value to affect the state of the parent which will be rendered with a different style than from the default rendered style.
At the moment the state updates fine but the component isn't re-rendered.
import React, {
AppRegistry,
Component,
Image,
ListView,
StyleSheet,
Text,
View,
Switch
} from 'react-native';
var SONGS_DATA = {
"songs" : [
{
"title" : "I Heard React Was Good",
"artist" : "Martin",
"played" : false
},
{
"title" : "Stack Overflow",
"artist" : "Martin",
"played" : false
}
]
}
class BasicSwitchExample extends Component{
constructor(props){
super(props);
this.state = {
played: false
};
this.handlePlayed = this.handlePlayed.bind(this);
}
handlePlayed(value){
console.log('handlePlayed value: ' + value);
this.setState({played: value});
this.props.callbackParent(value);
}
render() {
return <View> // this has to be on the same line or it causes an error for some reason
<Switch
onValueChange={this.handlePlayed}
style={{marginBottom: 10}}
value={this.state.played} />
</View>
}
}
class AwesomeProject extends Component {
constructor(props) {
super(props);
this.renderSong = this.renderSong.bind(this);
this.togglePlayed = this.togglePlayed.bind(this);
this.fetchData = this.fetchData.bind(this);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false
};
}
componentDidMount() {
this.fetchData();
}
togglePlayed(value) {
// this is never reached
this.setState({played: value});
console.log('Song has been played? ' + this.state.played);
}
fetchData() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(SONGS_DATA.songs),
loaded: true,
});
}
render() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderSong}
style={styles.listView}
/>
);
}
renderLoadingView() {
return (
<View style={styles.container}>
<Text>
Loading songs...
</Text>
</View>
);
}
renderSong(song) {
let bgStyle = this.state.played ? styles.played : styles.container;
console.log('style : ' + bgStyle); // prints 2 for some reason
return (
<View style={this.state.played ? styles.played : styles.container}>
<View style={styles.half}>
<Text style={styles.title}>{song.title}</Text>
<Text style={styles.artist}>{song.artist}</Text>
</View>
<View style={styles.half}>
<BasicSwitchExample callbackParent={this.togglePlayed} />
</View>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
/* styles here */
},
played: {
/* styles here */
},
});
AppRegistry.registerComponent('SampleApp', () => AwesomeProject);
React Native Playground
For some reason when rendering the style, it's printed out as 2. This only happens when the app is first loaded and is never reached after a switch is toggled.
You're not mutating any state that's relevant to the ListView rendering. In togglePlayed you have to change parts of the state that will change the rendered list: https://rnplay.org/apps/z0fKKA

React Native - Conditional Styles on Parent Based on Child State

I was following the React Native tutorial and have tried to adapt it to show a list of songs rather than movies and add in a toggling ability using the Switch component.
I managed to get this to work but now I am trying to send the value of the switch back to the parent so that a conditional style can be applied.
When I attempted to do this, I get an error saying
undefined is not an object (evaluating 'this.state.played')
which seems sensible since the console statement in the togglePlayed never seems to be called.
import React, {
AppRegistry,
Component,
Image,
ListView,
StyleSheet,
Text,
View,
Switch
} from 'react-native';
var SONGS_DATA = {
"songs" : [
{
"title" : "I Heard React Was Good",
"artist" : "Martin",
"played" : false
},
{
"title" : "Stack Overflow",
"artist" : "Martin",
"played" : false
}
]
}
var BasicSwitchExample = React.createClass({
getInitialState() {
return {
played: false
};
},
handlePlayed(value) {
console.log('Switch has been toggled, new value is : ' + value)
this.setState({played: value})
this.props.callbackParent(value);
},
render() {
return (
<View>
<Switch
onValueChange={this.handlePlayed}
style={{marginBottom: 10}}
value={this.state.played} />
</View>
);
}
});
class AwesomeProject extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
}
componentDidMount() {
this.fetchData();
}
getInitialState() {
return {
played: false
};
}
togglePlayed(value) {
// this is never reached
this.setState({played: value});
console.log('Song has been played? ' + this.state.played);
}
fetchData() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(SONGS_DATA.songs),
loaded: true,
});
}
render() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderSong}
style={styles.listView}
/>
);
}
renderLoadingView() {
return (
<View style={styles.container}>
<Text>
Loading songs...
</Text>
</View>
);
}
renderSong(song) {
return (
// not sure if this syntax is correct
<View style={this.state.played ? 'styles.container' : 'styles.played'}>
<View style={styles.half}>
<Text style={styles.title}>{song.title}</Text>
<Text style={styles.artist}>{song.artist}</Text>
</View>
<View style={styles.half}>
<BasicSwitchExample callbackParent={() => this.togglePlayed} />
</View>
</View>
);
}
}
var styles = StyleSheet.create({
/* styles here */
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
React Native Playground
Any pointers would be great as I am new to React and especially React Native.
You forgot to bind your function to your component, it should look like this
class BasicSwitchExample extends Component{
constructor(props){
super(props);
this.state = {
played: false
};
this.handlePlayed = this.handlePlayed.bind(this);
}
handlePlayed(value){
this.setState({played: value});
this.props.callbackParent(value);
}
render() {
return <View>
<Switch
onValueChange={this.handlePlayed}
style={{marginBottom: 10}}
value={this.state.played} />
</View>
}
}
class AwesomeProject extends Component {
constructor(props) {
super(props);
this.renderSong = this.renderSong.bind(this);
this.togglePlayed = this.togglePlayed.bind(this);
this.fetchData = this.fetchData.bind(this);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
}
componentDidMount() {
this.fetchData();
}
togglePlayed(value) {
// this is never reached
this.setState({played: value});
console.log('Song has been played? ' + this.state.played);
}
fetchData() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(SONGS_DATA.songs),
loaded: true,
});
}
render() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderSong}
style={styles.listView}
/>
);
}
renderLoadingView() {
return (
<View style={styles.container}>
<Text>
Loading songs...
</Text>
</View>
);
}
renderSong(song) {
return (
// not sure if this syntax is correct
<View style={this.state.played ? 'styles.container' : 'styles.played'}>
<View style={styles.half}>
<Text style={styles.title}>{song.title}</Text>
<Text style={styles.artist}>{song.artist}</Text>
</View>
<View style={styles.half}>
<BasicSwitchExample callbackParent={this.togglePlayed} />
</View>
</View>
);
}
}

Resources