Use modal to take user checkbox input and return data back to parent component - react-native

I have a Newevent class component which takes several user inputs. One of them is an array of user ids called access list and the rest is text input. Here is the code I am thinking about:
export default class NewEvent extends React.Component {
state = {
name: '',
access_list: [],
modalVisible: false
};
triggerModal() {
this.setState(prevState => {
return {
display: true
}
});
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder='Name'
autoCapitalize="none"
placeholderTextColor='white'
onChangeText={val => this.onChangeText('name', val)}
/>
<Button
onPress = { () => this.triggerModal() }
title = "Open Modal"
color = "orange">
</Button>
<DisplayModal
visible={this.state.modalVisible}
/>
<Button
title='Save'
onPress={this.save}
/>
</View>
)
}
}
In DisplayModal.js, it is a function component to display a series of checkbox allowing user to check which user he wants to include in access list:
import React from 'react'
import { Modal, View, Image, Text, StyleSheet } from 'react-native';
import { CheckBox } from 'react-native-elements'
const DisplayModal = (props) => (
<Modal visible={ props.display } animationType = "slide"
onRequestClose={ () => console.log('closed') }>>
<View>
<CheckBox
title='Click Here1'
checked={this.state.checked}
/>
<CheckBox
title='Click Here2'
checked={this.state.checked}
/>
</View>
</Modal>
)
Any recommendation about constructing DisplayModal.js to return the user's selection?

I'm not sure about the problem but I think that you want to send states from DisplayModal to Newsevent for that purpose at least there are two methods:
1- Using redux which is a little(or too) complicated. you can read the documentation.
2- use a props which is a function that returns data from the child component to the parent.
for example:
export class DisplayModal extends Component {
state = {
item1: false,
item2: false
};
_updateState = item => {
this.setState({ item: !this.state[item] });
// use the getState props to get child state
return this.props.getState(this.state);
};
render() {
return (
<Modal
visible={props.display}
animationType="slide"
onRequestClose={() => console.log("closed")}
>
>
<View>
<CheckBox
title="Click Here1"
checked={this.state.checked}
onPress={() => this._updateState("item1")}
/>
<CheckBox
title="Click Here2"
checked={this.state.checked}
onPress={() => this._updateState("item2")}
/>
</View>
</Modal>
);
}
}
and use the getState props to get user's selections:
<DisplayModal getState={(items)=> this.setState({userSelections:items}) />
after each action on DisplayModal i.e on checkbox press, _updateState call the function the function in props and in your example, the function gets data from the child and update the parent state.

Related

react hook form multiple control on one Controller

I have a text input component that uses text input from react native paper, I want to make a place autocomplete by calling google place autocomplete API
right now I can display the suggestion but I can't change the text input value with the value of the suggestion that has been clicked
screenshot of component
since I use Controller from react hook form I thought I could use setValue from useForm to change the value but it didn't do anything when I try to call setValue to change textInput value to one of the suggested value
import React from "react";
import { FlatList, StyleSheet, TouchableOpacity, View } from "react-native";
import { Text, TextInput, Colors } from "react-native-paper";
import { Controller, useForm } from "react-hook-form";
import axiosInstance from "services/axiosInstance";
export default React.forwardRef(
(
{
name,
label,
placeholder,
control,
style: addOnStyle,
...props
},
ref
) => {
const { setValue } = useForm();
const [addressList, setAddressList] = React.useState([])
const getAddressList = async (input) => {
if (input == null || input.match(/^ *$/) !== null) {
setAddressList([])
} else {
const response = await axiosInstance.get(
`https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${input}&components=country:us&language=en&key=API_KEY`
)
setAddressList([])
if (response?.data?.status === "OK") {
response?.data?.predictions?.map((item) => setAddressList(addressList => [...addressList, item.description]))
} else {
setAddressList(["Address not found."])
}
}
}
return (
<View style={{ ...styles.viewInput, ...addOnStyle }}>
<Controller
control={control}
name={name}
defaultValue=""
render={({
field: { onChange, onBlur, value, name },
fieldState: { error },
}) => {
return (
<>
<TextInput
label={label}
name={name}
placeholder={placeholder}
onBlur={onBlur}
onChangeText={(val) => onChange(val, getAddressList(val))}
error={!!error?.message}
value={value}
ref={ref}
{...props}
/>
{error?.message ? (
<Text style={styles.textError}>{error?.message}</Text>
) : null}
{addressList.length > 0 ?
<View style={styles.addressListContainer}>
<FlatList
keyExtractor={(_, i) => String(i)}
data={addressList}
renderItem={({ item, index }) => {
return (
<TouchableOpacity
activeOpacity={1}
style={[styles.addressListItem, index==0 ? {borderTopWidth: 0} : {borderTopWidth: 1}]}
onPress={() => {setAddressList([]), setValue(name, item)}}
>
<Text numberOfLines={1}>{item}</Text>
</TouchableOpacity>
)
}}
/>
</View>
: null}
</>
);
}}
/>
</View>
);
}
);
UPDATE Changed the title to match the current question
I think for now my problem is since the control is set from the outside of the component that makes it can't be changed with setValue from inside the component, now I wonder if we could use multiple control on one Controller?
I solve it by changing setValue(name, item) on onPress to onChange(item) it doesn't need another control

Passing Navigation to a Function Component

This Is My Home Page Code:
import React from "react";
//More Imports
export default class Home extends React.Component {
//Some Code
render() {
const { navigation } = this.props;
return (
<ScrollView>
//Some Code
<View style={styles.barContainer}>
<Button
title="Add Lesson"
onPress={() => navigation.navigate("ThisLesson")}
/>
</View>
//Some Code
{ScrollViewWithCards}
//Some Code
</ScrollView>
);
}
}
const styles = StyleSheet.create({
//Some Style
});
const cards = [
{
day: "3",
month: "Jan",
numberOfPeople: "4",
time: "17:00-18:00",
title: "Dance Class",
image: require("../../../assets/images/image1.jpeg"),
},
//More Cards...
];
const ScrollViewWithCards = (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
I'm mapping through an array of static data and rendering cards unto the screen
I made the cards pressable so that they take me to another page,
when I click the card it Returns an error:Reference Error: Can't find variable: navigation
But the Button Above the Cards Works Just Fine
What Am I Doing Wrong?
I tried the useNavigation Hook but it didn't work either
Update
This is my HomeCard component:
import React from "react";
//More Imports
const HomeCard = (props) => {
return (
<View style={styles.container}>
//Some Code
</View>
);
};
export default HomeCard;
const styles = StyleSheet.create({
//Some Style
});
const smallAvatars = [
//Some Array
];
I passed {navigation} to ScrollViewWithCards like so:
const ScrollViewWithCards =({navigation})=>()
but now I'm Getting another Error TypeError: undefined is not an object (evaluating 'navigation.navigate')
Solution
The Solution for this Problem is to transform ScrollViewWithCards to a function component, then pass props to it and add return:
const ScrollViewWithCards = (props) => {
return (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
props.navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
};
and then in the main render:
<ScrollViewWithCards navigation={this.props.navigation} />
You are setting the const navigation inside the render function, and it wont be accessible inside other functions, so you have to use
this.props.navigation.navigate
Then you can simply do
const ScrollViewWithCards =()=> (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
In the routing section, you need to mention the both component like this,
<Stack.Screen name="<your component name>" component={your component class} />
please don't forget to import the files at the above.
and then you can use the navigation props like,
this.props.navigation //for class component
props.navigation //for functional component
or if you have parent child relation in your compoent try this one:
<YOUR_COMPONENT navigation={props.navigation}/> // functional component
<YOUR_COMPONENT navigation={this.props.navigation}/> // class component

Change the color of Button when onFocus input

Good Morning , I tried a simple component with react-native that changes the color of my button while onFocus().I can't find how to change the color . Here is my component . Have you any ideas ?
import React, {Component} from 'react';
import {
StyleSheet,Text, View, Button,
} from 'react-native';
export default class App extends Component {
render() {
return (
<View style={styles.inputContainer}>
<TextInput
maxHeight={200}
style={styles.textInput}
ref={(r) => {
this.textInputRef = r;
}}
placeholder={'Message'}
underlineColorAndroid="transparent"
onFocus={()=>{/*Here i awant to change the color of Button }}
testID={'input'}
/>
<Button color="transparent" id="ScanButton"
onPress={() => this.setState({text: 'Placeholder Text'})}
title="Scan Barcode"
/>
</View>
)}
First Initialize your variable
constructor(props) {
super(props);
this.state = {
isFocus: false
}
}
In your TextInput add two props onFocus() and onBlur()
<TextInput
maxHeight={200}
style={styles.textInput}
ref={(r) => {
this.textInputRef = r;
}}
placeholder={'Message'}
underlineColorAndroid="transparent"
onBlur={() => this.onBlur()}
onFocus={() => this.onFocus()}
testID={'input'}
/>
add two methods in your class to change the state
onFocus() {
this.setState({
isFocus: true
})
}
onBlur() {
this.setState({
isFocus: false
})
}
and your button style will be like that
<Button
color={this.state.isFocus ? 'red' : 'green'}
id="ScanButton"
onPress={() => this.setState({text: 'Placeholder Text'})}
title="Scan Barcode"
/>
style={{color: this.props.focused ? '#8B327C' :'#3F8B99'}}
try something like this

In React-native, how to handle checkbox in Listview?

In my react-native app, I am trying to show my contact details with checkboxes for selecting.
Here is my code:
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData, sectionID, rowID) => (
<TouchableHighlight onPress={() => this.goRideDetails(rowData)}>
<Text style={styles.rideHeader}>{rowData.name} </Text>
<CheckBox
checked={this.state.checked}
onCheckBoxPressed={() =>
this.setState({ checked: !this.state.checked })
}
/>
</TouchableHighlight>
)}
/>
In my view checkbox is displaying on every row, but not working.
Any one can help me. Thanks in advance.
You can easily do this with component separation. Please, take a look here:
export default class ContactList extends Component {
static propTypes = {
contacts: React.PropTypes.array,
}
static defaultProps = {
contacts: [],
}
constructor(){
super();
this._renderRow = this._renderRow.bind(this);
}
_renderRow(rowData,sectionID,rowID) {
return <Contact info={ rowData } />;
}
render() {
return (
<ListView
dataSource={ this.props.contacts }
renderRow={ this._renderRow }
/>
);
}
}
export class ContactList extends Component {
static propTypes = {
info: React.PropTypes.object.isRequired,
}
constructor(){
super();
this.goRideDetails = this.goRideDetails.bind(this);
this.setChecked = this.setChecked.bind(this);
}
goRideDetails() {
//your logic here
}
setChecked() {
this.props.info.checked = !this.props.info.checked; //will be much better to do it with redux and action creators
}
render() {
return (
<TouchableHighlight onPress={ this.goRideDetails }>
<Text style={ styles.rideHeader }>{this.props.info.name} </Text>
<CheckBox checked={ this.props.info.checked } onCheckBoxPressed={ this.setChecked } />
</TouchableHighlight>
);
}
}
After that you can simply call:
<ContactList contacts={this.state.dataSource} />
in your jsx and voila.
Important note: Do not use array functions inside your jsx code blocks.
Important note 2: Try to start using redux or flux for storing state of your application. It will be provide much better code design.
Hope, it will help.
import React , {Component} from 'react'
import {
Text,
View,
ListView,
StyleSheet,
TouchableOpacity,
Image,
} from 'react-native'
import CheckBox from 'react-native-checkbox'
var Folder = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
var folder = '' ////// all the new folder
var check_folder = [] ////// all the check box conditions
class ApproveContent extends Component {
///////// all the upper thing that are global variable for this script is has same value as that of the state the only reason we are using this because of the layout update //////////
state={
folder:[],
data:[],
check:[]/////// this need to do just to upadte the layout of the check box
}
render(){
return(
<View style = {{flex:1,backgroundColor:'white',alignItems:'center'}}>
<ListView
dataSource={Folder.cloneWithRows(this.state.folder)}
renderRow={(rowData,rowID,sectionID) => <View style = {{ alignItems: 'center',margin:5}}>
<TouchableOpacity style = {{width:Dimension.ScreenWidth/1.2,height:Dimension.ScreenHeight/6,flexDirection: 'row',alignItems:'center'}}
onPress={() => {}}>
<CheckBox
label=''
labelBefore={false}
checked={this.state.check[sectionID]}
checkboxStyle = {{marginLeft: 20}}
onChange={(checked) => {
this.setState({
check:!this.state.check
})
if(check_folder[sectionID] == false){
check_folder[sectionID] = true
this.setState({
check:check_folder// has to do this because we cant change the single element in the array
})
}else{
check_folder[sectionID] = false
this.setState({
check:check_folder// has to do this because we cant change the single element in the array
})
}
console.log(check_folder)a
}}
/>
</TouchableOpacity>
</View>
}
/>
</View>
)}
}
export default ApproveContent
const style = StyleSheet.create({
TextStyle:{
fontFamily: 'Roboto-Bold',
fontSize:15,
},
approveButton: {
bottom:0,
left:0,
alignItems: 'center',
}
})

undefined is not an object(evaluating this.props.navigator.push)

I have two page i.e sign in page and a payment page.
I am trying to navigate to payment page on tap on SignIn button, but i am getting error undefined is not an object(evaluating this.props.navigator.push)
The code is as below:
import React, {
StyleSheet,
Text,
View,
TextInput,
Component,
Alert,
Navigator
} from 'react-native';
var Button = require('react-native-button');
import Payments from '../payments'
class Signin extends Component{
onSubmitPressed(){
this.props.navigator.push({
title: 'secure Page',
component: <Payments/>
});
};
render() {
return(
<View style={styles.container}>
<View style={styles.Inputview}>
<TextInput id='user' style={styles.input}
placeholder={'Username'}
/>
<TextInput id='Password' secureTextEntry={true}
placeholder={'Password'}
onChangeText={password => this.setState({password})}
/>
</View>
<View >
<Button style={styles.Register}
onPress={(this.onSubmitPressed)}>
Sign In
</Button>
</View>
</View>
)
}
}
export default Signin
If any one let me know how to solve this issue??
You need to set up your Navigator and initial route as the entry point as opposed to a regular component. Try something like this:
(Also set up a working example here)
https://rnplay.org/apps/iKx2_g
class App extends Component {
renderScene (route, navigator) {
return <route.component navigator={navigator} />
}
render() {
return (
<Navigator
style={styles.container}
renderScene={this.renderScene.bind(this)}
initialRoute={{component: SignIn}}
/>
);
}
}
class SignIn extends Component {
_navigate () {
this.props.navigator.push({
component: Payments
})
}
render () {
return (
<View>
<Text>Hello from SignIn</Text>
<Button onPress={this._navigate.bind(this)} />
</View>
)
}
}
class Payments extends Component {
render () {
return (
<Text>Hello from Payments</Text>
)
}
}
First you need to bind the this to the function onSubmitPressed. And make sure that you have passed navigator object to this component on the renderScene function of the navigator.
// binding this to onSubmitPressed
<Button style={styles.Register}
onPress={this.onSubmitPressed.bind(this)}
>
Sign In
</Button>
// binding this to on SubmitPressed using arrow function
<Button style={styles.Register}
onPress={() => this.onSubmitPressed()}
>
Sign In
</Button>