import React, { Component, useState } from "react";
export class Register extends Component {
constructor(props) {
super(props);
this.state = {
In: "",
lbs: "",
age: "",
gender: "",
};
}
render() {
//BMR Calculator
const { In } = this.state;
const { lbs } = this.state;
const { age } = this.state;
const { gender } = this.state;
return (
<SafeAreaView>
<View>
<TextInput
label="username"
onChangeText={(name) => this.setState({ name })}
/>
<TextInput
label="email"
onChangeText={(email) => this.setState({ email })}
/>
<TextInput
label="password"
onChangeText={(password) => this.setState({ password })}
/>
</View>
<Text>Weight</Text>
<TextInput
placeholder="In"
keyboardType="numeric"
maxLength="2"
onChangeText={(In) => this.setState({ In })}
style={styles.heightImperialIn}
/>
<Text>Weight</Text>
<TextInput
placeholder="lbs"
keyboardType="numeric"
maxLength="3"
value={lbs}
onChangeText={(lbs) => this.setState(+e.target.value)}
//onChangeText={(lbs) => this.setState({ lbs })}
style={styles.weigthImperial}
/>
<Text style={styles.txtAge}>Age</Text>
<TextInput
placeholder="18"
keyboardType="numeric"
maxLength="2"
value={age}
onChange={(age) => this.setState(+e.target.value)}
//onChangeText={(age) => this.setState({ age })}
style={styles.ageImperial}
/>
<Text>Gender</Text>
<View style={{ top: hp("10%") }}>
<RNPickerSelect
style={pickerStyle}
placeholder={{
label: "Select a gender",
value: null,
}}
onValueChange={(gender) => this.setState({ gender })}
items={[
{ label: "Male", value: "male" },
{ label: "Female", value: "female" },
]}
/>
</View>
</SafeAreaView>
Hey, everyone, so I have these two equations for a BMR calculator :
male=66+(6.2lbs)+(12.7In)-(6.76*age)
female=655.1+(4.35lbs)+(4.7In)-(4.7*age)
My question is how do I get the values of lbs, In and age from the text inputs above and inserted them in one of the two equations, depending on the gender selected by the user, and display the result?
Thanks
Here is the working example: Expo Snack
import React, { Component, useState } from 'react';
import { Text, View, StyleSheet, SafeAreaView, TextInput } from 'react-native';
import Constants from 'expo-constants';
import RNPickerSelect from 'react-native-picker-select';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class Register extends Component {
constructor(props) {
super(props);
this.state = {
In: 0,
lbs: 0,
age: 0,
gender: '',
};
}
render() {
//BMR Calculator
const { In, lbs, age, gender } = this.state;
return (
<SafeAreaView>
<View>
<TextInput
label="username"
onChangeText={(name) => this.setState({ name })}
/>
<TextInput
label="email"
onChangeText={(email) => this.setState({ email })}
/>
<TextInput
label="password"
onChangeText={(password) => this.setState({ password })}
/>
</View>
<Text>Weight</Text>
<TextInput
placeholder="In"
keyboardType="numeric"
maxLength="2"
onChangeText={(In) => this.setState({ In: Number(In) })}
style={styles.heightImperialIn}
/>
<Text>Weight</Text>
<TextInput
placeholder="lbs"
keyboardType="numeric"
maxLength="3"
value={lbs}
onChangeText={(lbs) => {
console.log(typeof Number(lbs));
this.setState({ lbs: Number(lbs) });
}}
/>
<Text style={styles.txtAge}>Age</Text>
<TextInput
placeholder="18"
keyboardType="numeric"
maxLength="2"
onChangeText={(age) => this.setState({ age: Number(age) })}
style={styles.heightImperialIn}
/>
<Text>Gender</Text>
<View style={{}}>
<RNPickerSelect
style={{}}
placeholder={{
label: 'Select a gender',
value: null,
}}
onValueChange={(gender) => this.setState({ gender })}
items={[
{ label: 'Male', value: 'male' },
{ label: 'Female', value: 'female' },
]}
/>
</View>
<Text>Result for {gender.toUpperCase()}</Text>
{gender ? (
gender === 'male' ? (
66 + 6.2 * lbs + 12.7 * In + 6.76 * age
).toFixed(2) : (
65.51 + 4.35 * lbs + 4.7 * In - 4.7 * age
).toFixed(2)
) : (
<Text>Select the values</Text>
)}
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
});
Related
I'm trying to fill in the 'ans' fields in an array of inputs, but when I type in one of the inputs, it overrides all other objects in the state. How do I update the 'ans' fields in the object array, when I type something in one of the inputs, in the correct way, without it overwriting the other objects in the array within the state?
My code:
import React, { useState } from "react";
import {
StyleSheet,
View,
Text,
KeyboardAvoidingView,
ScrollView,
TextInput,
Button,
} from "react-native";
export default function List({ navigation }) {
const [data, setData] = useState({
"creator": {
"creatorAuthor": "Alisson",
"user": "",
},
"customField": [
{
"ans": "",
"quests": "Quest 1",
},
{
"ans": "",
"quests": "Quest 2",
},
{
"ans": "",
"quests": "Quest 3",
},
],
"date": {
"dateCreator": "01/01/2021",
"dateUser": "",
},
"id": "-MQxtMmc_K7rDIgutcNC",
"title": "Google",
}
);
return (
<KeyboardAvoidingView style={{backgroundColor: 'white', flex: 1}} enabled>
<ScrollView contentContainerStyle={{ padding: 10 }}>
<View>
<Text>{data.title}</Text>
</View>
<View>
<Text>{ data.creator.creatorAuthor}</Text>
<TextInput
style={{marginBottom: 20}}
placeholderTextColor= '#808080'
value={data.creator.user}
onChangeText={value => setData({ ...data, creator: {...data.creator, user: value} })}
placeholder='Usuário'
multiline={true}
/>
</View>
<View>
<Text>{data.date.dateCreator}</Text>
<TextInput
style={{marginBottom: 20}}
placeholderTextColor= '#808080'
value={data.date.dateUser}
onChangeText={value => setData({ ...data, date: {...data.date, dateUser: value} })}
placeholder='Data das repostas'
/>
</View>
{
data.customField.map((q, id) => (
<View key={id}>
<Text>{q.quests}</Text>
<TextInput
style={{marginBottom: 20}}
placeholderTextColor= '#808080'
value={q.ans}
onChangeText={value => setData({ ...data, customField: [{...q, ans: value}] })}
placeholder='Resposta'
multiline={true}
/>
</View>
))
}
<Button
title='Salvar'
onPress={() => {}}
/>
</ScrollView>
</KeyboardAvoidingView>
);
}
This will only produce an array of one value:
onChangeText={value => setData({ ...data, customField: [{...q, ans: value}] })}
You have to copy over the original array and replace the value you want to update:
onChangeText={value => setData({ ...data, customField: [data.customField.filter(cf => cf.quests !== q), {...q, ans: value}] })}
I didn't test the syntax, but should be something like this.
I would recommend to split the data into multiple state fields as it will allow you simpler syntax and will be more obvious what you doing:
const [customField, setCustomField] = useState({ -your custom field data here-});
{
customField.map((q, id) => (
<View key={id}>
<Text>{q.quests}</Text>
<TextInput
style={{marginBottom: 20}}
placeholderTextColor= '#808080'
value={q.ans}
onChangeText={value => setCustomField([ customField.filter(cf => cf.quests !== q), {...q, ans: value}]}
placeholder='Resposta'
multiline={true}
/>
</View>
))
}
Currently, this - is how the SearchBar and FlatList is showing up on the screen. Upon clicking on the SearchBar and typing one of the list components, it shows up this- way. I want the FlatList to only appear when I click on the SearchBar. How do I implement this in the app? Something like a dropdown search bar...
import React from 'react';
import { View, Text, FlatList, ActivityIndicator, StyleSheet } from 'react-native';
import { SearchBar } from 'react-native-elements';
import { Button, Menu, Divider, Provider, TextInput } from 'react-native-paper';
const restaurantList = [
{
type: 'Italian',
name: 'DiMaggio'
},
{
type: 'Greek',
name: 'Athena'
}
];
export default class App extends React.Component {
static navigationOptions = {
title: 'Search for Restaurants'
};
constructor (props) {
super(props);
this.state = {
loading: false,
data: restaurantList,
error: null,
value: '',
};
this.arrayholder = [];
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%'
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text
});
const newData = restaurantList.filter(item => {
console.log(item.type)
const itemData = `${item.name.toUpperCase()} ${item.type.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.includes(textData);
});
this.setState({
data: newData
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type..."
value={this.state.value}
onChangeText={text => this.searchFilterFunction(text)}
/>
);
};
render () {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
} else {
return (
<Provider>
<View
style={{
paddingTop: 50,
flexDirection: 'row',
justifyContent: 'center',
}}>
<View style={styles.container}>
<FlatList
keyExtractor={(item, index) => `${index}`}
extraData={this.state}
data={this.state.data}
renderItem={({ item }) => (
<Text>{item.name} {item.type}</Text>
)}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
</View>
<View style={styles.container}>
<TextInput />
</View>
</Provider>
);
}
}
}
Try this way
this.state = {
searchClicked: false
};
<SearchBar
placeholder="Type..."
value={this.state.value}
onChangeText={text => this.searchFilterFunction(text)}
onFocus={() => this.setState({searchClicked: true})}
onBlur={() => this.setState({searchClicked: false})}
/>
<View>{this.renderHeader()}</View>
{this.state.searchClicked && <FlatList
keyExtractor={(item, index) => `${index}`}
extraData={this.state}
data={this.state.data}
renderItem={({ item }) => (
<Text>{item.name} {item.type}</Text>
)}
ItemSeparatorComponent={this.renderSeparator}
// ListHeaderComponent={this.renderHeader} <—- Remove from here —->
/>}
In case #Lakhani reply doesn't satisfy your question, I have another suggestion for you.
Your SearchBar should contain a TextInput. You can use onFocus and onBlur props to capture event you click on SearchBar or leave it.
...
<SearchBar
placeholder="Type..."
value={this.state.value}
onChangeText={text => this.searchFilterFunction(text)}
onFocus={()=>this.setState({showList: true})} // <---
onBlur={()=>this.setState({showList: false})} // <---
/>
...
render() {
return (
...
{
this.state.showList && <FlatList ... />
}
)
}
I am using formik and react-native-formik to create form. I am using my custom component for textinput in formik form.
My custom component code :
// import statements ...
class FormInput extends Component {
focus = () => { this.textInputField.focus(); }
render() {
const {props} = this;
return (
<TextInput
{...this.props}
placeholder={props.placeholder}
ref={(ref) => { this.textInputField = ref }}
style={{ height: 50, borderWidth: 1, margin: 5 }}
/>
)
}
}
export default FormInput
Then I create Form from below code :
const FormInput2 = compose(
handleTextInput,
withNextInputAutoFocusInput
)(FormInput);
const Form = withNextInputAutoFocusForm(View);
Then I have created original form as below :
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
keyboardShouldPersistTaps="handled"
>
<Formik
initialValues={{ firstName: '', email: '', password: '' }}
onSubmit={values => { }}
validationSchema={signUpValidationSchema}
render={({ values, handleChange, errors, setFieldTouched, touched, isValid, handleSubmit }) => (
<Form>
<FormInput2
icon="user"
placeholder="First Name"
value={values.firstName}
onChangeText={handleChange('firstName')}
onBlur={() => setFieldTouched('firstName')}
/>
{touched.firstName && errors.firstName && (
<CText style={{ fontSize: 10, color: 'red' }}>{errors.firstName}</CText>
)}
{/*
...
...
Others Fiels
*/}
<Button
title="SIGN UP"
disabled={!isValid}
color={Colors.primary}
onPress={handleSubmit}
/>
</Form>
)}
/>
</ScrollView>
But, I am getting done on each and every field keyboard return type. If I press done then form submit method fires.
Can anyone help me how can I implement auto focus ?
I have also tried exporting my custom component as below, but it is also not working :
export default withFormikControl(FormInput)
If anyone is also having the same error then here is the solution...
Custom component should have name property and the order of the component should be the same as initialValues.
Means if initialValues as below :
initialValues={{ firstName: '', lastName: '', email: '', phone: '', password: '', confirmPassword: '' }}
Then first should be firstName field then lastName, email and so on...
So I added name prop in custom component and worked autofocus.
<FormInput2
icon="user"
placeholder="First Name"
value={values.firstName}
label="First Name"
name="firstName" // added this
type="name"
onChangeText={handleChange('firstName')}
onBlur={() => setFieldTouched('firstName')}
/>
{
touched.firstName && errors.firstName && (
<CText style={{ fontSize: 10, color: 'red' }}>{errors.firstName}</CText>
)
}
<FormInput2
label="Last Name"
name="lastName" // added this
type="name"
icon="user"
placeholder="Last Name"
value={values.lastName}
onChangeText={handleChange('lastName')}
onBlur={() => setFieldTouched('lastName')}
/>
{
touched.lastName && errors.lastName && (
<CText style={{ fontSize: 10, color: 'red' }}>{errors.lastName}</CText>
)
}
I have used redux-form for the form. I have developed a wizard considering an example from the documentation. The wizard is working but when i go to step 2 i.e SyncAccount component which has the form and fill that form and hit the next button to go to another step and hit the back button to go back to the SyncAccount component then the form is cleared. I have used destroyOnUnmount but it is not solving my issue either. What have i missed?
only second page i.e SyncAccount has only form. First and third do not have.
here is the code
AccountSteps.js
const mapDispatchToProps = dispatch => ({
getUser: () => dispatch(getCurrentUser()),
postWizard: (userObj, wizardType) =>
dispatch(postWizardData(userObj, wizardType)),
updateWizard: (userObj, wizardType) =>
dispatch(updateWizardData(userObj, wizardType)),
});
const mapStateToProps = state => ({
userData: state.userReducer,
wizard: state.wizardReducer,
});
class AccountSteps extends React.Component<{
user: Object,
wizard: Object,
postWizard: Function,
updateWizard: Function,
getUser: Function
}> {
constructor(props) {
super(props);
this.state = {
page: 0,
steps: [
{ steps: 0, label: 'Privacy' },
{ steps: 1, label: 'Sync Your Account' },
{ steps: 2, label: 'Install Extension' },
],
};
}
componentDidMount() {
this.props.getUser();
}
static getDerivedStateFromProps(nextProps, prevState) {
const { wizard } = nextProps;
if (!isEmpty(wizard) && wizard.page !== prevState.page) {
return {
page: wizard.page,
};
}
return null;
}
nextPage = (userObj, type) => {
this.props.postWizard(userObj, type);
};
previousPage = (wizardData) => {
console.log('wizardData', wizardData);
this.props.updateWizard(wizardData);
};
render() {
const { page, steps } = this.state;
return (
<Wrapper>
<CardWrapper>
<Stepper activeStep={page} alternativeLabel>
{steps.map(step => (
<Step key={step.steps}>
<StepLabel>{step.label}</StepLabel>
</Step>
))}
</Stepper>
{page === 0 && (
<Privacy
{...this.props}
activeStep={page}
back={this.previousPage}
next={this.nextPage}
/>
)}
{page === 1 && (
<SyncAccount
{...this.props}
activeStep={page}
back={this.previousPage}
next={this.nextPage}
/>
)}
{page === 2 && (
<Extension
{...this.props}
activeStep={page}
back={this.previousPage}
next={this.nextPage}
/>
)}
</CardWrapper>
</Wrapper>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(AccountSteps);
SyncAccount.js
const SyncAccount = ({
user,
emailProvider,
handleProviderChange,
handleChange,
...props
}: {
user: Object,
emailProvider: string,
handleProviderChange: Function,
handleChange: Function
}) => {
console.log('props in Sync', user.email);
return (
<SyncWrapper>
<IconsWrapper>
<CustomSync style={{ fontSize: 30 }} color="action" />
<Mail style={{ fontSize: 50 }} color="secondary" />
</IconsWrapper>
<p>Please enter your email address which you want to sync with us.</p>
<FormWrapper>
<span>Email Provider: </span>
<RadioGroup
aria-label="emailProvider"
name="provider"
style={{ display: 'flex', flexDirection: 'row' }}
value={user.provider}
onChange={handleChange}
>
<FormControlLabel
value="google"
control={<Radio color="primary" />}
label="Google"
/>
<FormControlLabel
value="imap"
control={<Radio color="primary" />}
label="IMAP"
/>
</RadioGroup>
<StyledField
label="Email"
id="email"
name="email"
type="email"
value={user.email}
onChange={handleChange}
placeholder="Email"
component={GTextField}
required
/>
<StyledField
label="Password"
id="password"
name="password"
type="password"
value={user.password}
onChange={handleChange}
placeholder="Password"
component={GPasswordField}
required
/>
<StyledField
label="Job title"
id="jobTitle"
name="job_title"
value={user.job_title}
onChange={handleChange}
placeholder="Job Title"
component={GAutoCompleteField}
required
/>
<Footer
{...props}
userObj={user}
type="sync"
wizardData={{ step_index: 0, wizard_name: 'privacy' }}
disabled={user && (!user.email || !user.password || !user.job_title)}
/>
</FormWrapper>
</SyncWrapper>
);
};
export default enhance(SyncAccount);
enhance.js
const requiredFields = {
email: 'Email',
password: 'Password',
job_title: 'Job Title',
};
const withReduxForm = reduxForm({
form: 'syncAccount',
fields: requiredFields,
destroyOnUnmount: false,
forceUnregisterOnUnmount: true,
validate,
});
const mapStateToProps = state => ({
wizard: state.wizardReducer,
});
const enhance = compose(
connect(
mapStateToProps,
null,
),
withReduxForm,
withState('user', 'updateUser', {
email: '',
password: '',
job_title: '',
provider: 'google',
wizard_name: 'extension',
step_index: 2,
}),
withHandlers({
handleChange: props => (ev) => {
if (ev.target) {
return props.updateUser({
...props.user,
[ev.target.name]: ev.target.value,
});
}
return props.updateUser({
...props.user,
job_title: ev.name,
});
},
}),
);
export default enhance;
I'm using https://facebook.github.io/react-native/docs/navigation.html by the way.
I'm trying to use the StackNavigator to go from Login.js to AboutDendro.js. What's wrong in my <Button/> component that's throwing that error in my iOS simulator?
Here's Login.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ScrollView, Text, TextInput, View, Button, StyleSheet } from 'react-native';
import { login } from '../redux/actions/auth';
import {AuthenticationDetails, CognitoUser, CognitoUserAttribute, CognitoUserPool} from '../lib/aws-cognito-identity';
import StackNavigator from 'react-navigation';
import AboutDendro from './AboutDendro';
const awsCognitoSettings = {
UserPoolId: 'something',
ClientId: 'something'
};
class Login extends Component {
constructor (props) {
super(props);
this.state = {
page: 'Login',
username: '',
password: ''
};
}
get alt () { return (this.state.page === 'Login') ? 'SignUp' : 'Login'; }
handleClick (e) {
e.preventDefault();
const userPool = new CognitoUserPool(awsCognitoSettings);
// Sign up
if (this.state.page === 'SignUp') {
const attributeList = [
new CognitoUserAttribute({ Name: 'email', Value: this.state.username })
];
userPool.signUp(
this.state.username,
this.state.password,
attributeList,
null,
(err, result) => {
if (err) {
alert(err);
this.setState({ username: '', password: '' });
return;
}
console.log(`result = ${JSON.stringify(result)}`);
this.props.onLogin(this.state.username, this.state.password);
}
);
} else {
const authDetails = new AuthenticationDetails({
Username: this.state.username,
Password: this.state.password
});
const cognitoUser = new CognitoUser({
Username: this.state.username,
Pool: userPool
});
cognitoUser.authenticateUser(authDetails, {
onSuccess: (result) => {
console.log(`access token = ${result.getAccessToken().getJwtToken()}`);
this.props.onLogin(this.state.username, this.state.password);
},
onFailure: (err) => {
alert(err);
this.setState({ username: '', password: '' });
return;
}
});
}
}
togglePage (e) {
this.setState({ page: this.alt });
e.preventDefault();
}
static navigationOptions = {
title: 'AboutDendro',
};
render() {
const { navigate } = this.props.navigation;
const App = StackNavigator({
Home: { screen: Login },
Profile: { screen: AboutDendro },
});
return (
<ScrollView style={{padding: 20}}>
<Button
title="Go to Jane's profile"
onPress={() =>
navigate('AboutDendro', { name: 'AboutDendro' })
}
/>
<Text style={{fontSize: 27}}>{this.state.page}</Text>
<TextInput
placeholder='Email Address'
autoCapitalize='none'
autoCorrect={false}
autoFocus={true}
keyboardType='email-address'
value={this.state.username}
onChangeText={(text) => this.setState({ username: text })} />
<TextInput
placeholder='Password'
autoCapitalize='none'
autoCorrect={false}
secureTextEntry={true}
value={this.state.password}
onChangeText={(text) => this.setState({ password: text })} />
<View style={{margin: 7}}/>
<Button onPress={(e) => this.handleClick(e)} title={this.state.page}/>
<View style={styles.firstView}>
<Text onPress={(e) => this.togglePage(e)} style={styles.buttons}>
{this.alt}
</Text>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
buttons: {
fontSize: 12,
color: 'blue',
flex: 1
},
firstView: {
margin: 7,
flexDirection: 'row',
justifyContent: 'center'
}
});
const mapStateToProps = (state, ownProps) => {
return {
isLoggedIn: state.auth.isLoggedIn
};
}
const mapDispatchToProps = (dispatch) => {
return {
onLogin: (username, password) => { dispatch(login(username, password)); }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
It is because navigation is not in your props. It is a part of your App component you created. But you do nothing with this component.
You should have an App.js file, with your stackNavigator, set your Login component as your default component in your stackNavigator's parameters.
Take a look at this documentation
I try to refactor your code.
in component render maybe you can just write :
render() {
const { navigate } = this.props.navigation;
return (
<ScrollView style={{padding: 20}}>
<Button
title="Go to Jane's profile"
onPress={() =>
navigate('Profile', { name: 'AboutDendro' })
}
/>
<Text style={{fontSize: 27}}>{this.state.page}</Text>
<TextInput
placeholder='Email Address'
autoCapitalize='none'
autoCorrect={false}
autoFocus={true}
keyboardType='email-address'
value={this.state.username}
onChangeText={(text) => this.setState({ username: text })} />
<TextInput
placeholder='Password'
autoCapitalize='none'
autoCorrect={false}
secureTextEntry={true}
value={this.state.password}
onChangeText={(text) => this.setState({ password: text })} />
<View style={{margin: 7}}/>
<Button onPress={(e) => this.handleClick(e)} title={this.state.page}/>
<View style={styles.firstView}>
<Text onPress={(e) => this.togglePage(e)} style={styles.buttons}>
{this.alt}
</Text>
</View>
</ScrollView>
);
}
}
And you separate component StackNavigation below component render() like:
const App = StackNavigator({
Home: { screen: Login },
Profile: { screen: AboutDendro },
});
And then import component App in your index.ios.js like:
import './Login';
just that. Maybe my answer can help you.