Redux-Form: Selecting form values from array of objects - react-native

I'm trying to select form values from array and show them below the input section. I know how to do it with individual fields but arrays are confusing me.
Here I am rendering array of fields:
export const renderAddressFields = ({ fields, meta: { touched, error } }) => (
<View>
{touched && error && <Text style={styles.ErrorMessage} >{error}</Text>}
{fields.map((ad, index) =>
<View key={index}>
<Text>Address #{index + 1}</Text>
<View style={styles.AddressFields}>
<Field
name={`${ad}.StreetName`}
type="default"
component={renderField}
label="Street Name"/>
<Field
name={`${ad}.Number`}
type="numeric"
component={renderField}
label="Street Number"/>
</View>
</View>
)}
</View>
)
And here is my (unsuccessful) attempt to select individual values from array of fields:
const { handleSubmit, mySubmit, fullAddress} = this.props
...
<Text>{fullAddress}</Text>
...
Form = reduxForm({
form: 'registerForm', // a unique identifier for this form
validate,
})(withRouter(Form))
const selector = formValueSelector('registerForm') // <-- same as form name
Form = connect(
state => {
const { StreetName, Number } = selector(state, { Addresses: [{ StreetName: 'StreetName', Number: 'Number' }] })
return {
fullAddress: `${StreetName || ''} ${ Number || ''}`
}
}
)(Form)
export default Form

your selector looks like a problem source.
do it something like this :
renderAddressFields = connect(state => {
let nameValue = selector(state, "nameofyourfieldarray[0].StreetName");
return {
nameValue
};
})(renderAddressFields)
then use it like:
export const renderAddressFields = ({ fields, meta: { touched, error }, nameValue })

Related

Limit number of checkboxes selected and save value

I am building a food delivery application, and I would like to know how I can limit the number of checkboxes selected. An example is when entering the subsidiary, it displays a list of products. If I select a pizza, there is an extras section that limits the number of extras you can select, if you want to select more than two and your limit is two it should not allow you
all this with react hooks, I attach a fragment of my component
const ExtrasSelector = ({options = [{}], onPress = () => {}, limit = 0}) => {
const [showOptions, setShowOptions] = useState(true);
const [selectedAmount, setSelectedAmount] = useState(0);
const EXTRA = ' extra';
const EXTRAS = ' extras';
const updatedList = options.map(data => ({
id: data.id,
name: data.name,
price: data.price,
selected: false,
}));
const [itemsList, setItemsList] = useState(updatedList);
const toggleOptions = () => setShowOptions(!showOptions);
useEffect(() => {
}, [selectedAmount]);
// onPress for each check-box
const onPressHandler = index => {
setItemsList(state => {
state[index].selected = !state[index].selected;
onPress(state[index], getSelectedExtras(state));
// Increments or decreases the amount of selected extras
if (state[index].selected) {
setSelectedAmount(prevState => prevState + 1);
} else {
setSelectedAmount(prevState => prevState - 1);
}
return state;
});
};
const getSelectedExtras = extrasArr => {
const selectedExsArr = [];
extrasArr.map(item => {
if (item.selected) {
selectedExsArr.push(item);
}
});
return selectedExsArr;
};
return (
<View>
<View style={styles.container}>
<TouchableOpacity style={styles.row} onPress={toggleOptions}>
<Text style={styles.boldTitleSection}>
Extras {'\n'}
<Text style={titleSection}>
Selecciona hasta {limit}
{limit > 1 ? EXTRAS : EXTRA}
</Text>
</Text>
<View style={styles.contentAngle}>
<View style={styles.contentWrapperAngle}>
<Icon
style={styles.angle}
name={showOptions ? 'angle-up' : 'angle-down'}
/>
</View>
</View>
</TouchableOpacity>
{showOptions ? (
itemsList.map((item, index) => (
<View key={index}>
<CheckBox
label={item.name}
price={item.price}
selected={item.selected}
otherAction={item.otherAction}
onPress={() => {
onPressHandler(index, item);
}}
/>
<View style={styles.breakRule} />
</View>
))
) : (
<View style={styles.breakRule} />
)}
</View>
</View>
);
};
This is a simple react implementation of "checkboxes with limit" behaviour with useReducer. This way the business logic (here the limitation but can be any) is implemented outside of the component in a pure js function while the component itself is just a simple reusable checkbox group.
const { useReducer } = React; // --> for inline use
// import React, { useReducer } from 'react'; // --> for real project
const reducer = (state, action) => {
if (state.checkedIds.includes(action.id)) {
return {
...state,
checkedIds: state.checkedIds.filter(id => id !== action.id)
}
}
if (state.checkedIds.length >= 3) {
console.log('Max 3 extras allowed.')
return state;
}
return {
...state,
checkedIds: [
...state.checkedIds,
action.id
]
}
}
const CheckBoxGroup = ({ data }) => {
const initialState = { checkedIds: [] }
const [state, dispatch] = useReducer(reducer, initialState)
return (
<table border="1">
{data.map(({ id, label }) => (
<tr key={id}>
<td>
<input
onClick={() => dispatch({ id })}
checked={state.checkedIds.includes(id)}
type="checkbox"
/>
</td>
<td>
{label}
</td>
</tr>
))}
</table>
)
};
const data = [
{ id: "1", label: "Mashroom" },
{ id: "2", label: "Ham" },
{ id: "3", label: "Egg" },
{ id: "4", label: "Ananas" },
{ id: "5", label: "Parmesan" },
]
ReactDOM.render(<CheckBoxGroup data={data} />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Textinput minimum length React native

Is there a way to limit the textinput between a minimum length and maximum length. Suppose I want to limit the textinput length between 5 and 15, how do I do that ?
Consider adding the following code in your component:
<TextInput onTextChange={this.onTextChange} maxLength={15} ... />
<Button onPress={this.onPress} ... >Submit</Button>
onTextChange = text => {
this.setState({text : text});
}
onPress = () => {
const {text} = this.state;
if(text.length < 5) {
console.log('Your text is less than what is required.');
}
}
You can do it using redux-form, following below steps
we.js
module.exports = {
reqMsg: 'Required',
maxLength: max => value => value && value.length > max ? `Must be ${max} characters or less` : undefined,
minValue: min => value => value && value.length < min ? `Must be at least ${min} characters` : undefined,
};
validations.js
import { reqMsg, maxLength, minValue } from './we';
module.exports = {
//Validation
required: value => value ? undefined : reqMsg,
maxLength15: maxLength(15),
minValue5: minValue(5)
};
UserCreateForm.js
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { Item, Input, CheckBox, ListItem, Spinner, Icon } from 'native-base';
import { required, minValue5, maxLength15} from './validations';
const renderField = ({ secureTextEntry, iconType, iconName, keyboardType, placeholder, meta: { touched, error, warning }, input: { onChange, ...restInput } }) => {
return (
<View>
<Item error={touched && !!error} rounded>
<Icon type={iconType} name={iconName} />
<Input secureTpickerStyleextEntry={JSON.parse(secureTextEntry)} keyboardType={keyboardType}
onChangeText={onChange} {...restInput} placeholder={placeholder} autoCapitalize='none'>
</Input>
{touched && !!error && <Icon name='close-circle' />}
</Item>
{touched && (!!error && <Text>{error}</Text>)}
</View>
);
};
class UserComponent extends Component {
render() {
return (
<Field name="Name" iconType="SimpleLineIcons" iconName="user" secureTextEntry="false" keyboardType="default" placeholder="FirstName LastName NikeName" component={renderField}
validate={[required, minValue5, maxLength15]}
/>
);
}
}
const UserCreateForm = reduxForm({
form: USER_CREATE_FORM // a unique identifier for this form
})(UserComponent);
export default UserCreateForm;
Previous comment is also Good, but it have more time and space complexity. For this overcome use this code .
<TextInput onTextChange={this.onTextChange} maxLength={15} ... />
onTextChange=()=>{
if (value ==^[a-zA-Z0-9]{5,15}$) {
alert( "Input is valid\n");
} else {
alert( "Input is invalid\n");
}
}
this code help me use this code, you can also reset the limit length, change the value
here 5 :- minimum
15:- maximum value.

Select single checkbox from listview in React-native

I want to select only one checkbox, not multiple.
If i select two checkboxes one by one the previously selected checkbox should be unselected.
In my below code i can select multiple checkboxes.
import React ,{Component} from "react";
import CircleCheckBox, {LABEL_POSITION} from "react-native-circle-checkbox";
class Select_Delivery_Option extends React.Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
});
this.state = {
check_data:[],
dataSource: ds.cloneWithRows([]),
checked:false,
isLoading:false,
};
}
//I had call The componentDidMount for json Data here and bind it in Data source;
render() {
return ();
}
_renderRow(rowData: string, sectionID: number, rowID: number) {
return (
<View style={{ flex:1,flexDirection:'column',backgroundColor:'#FFF'}}>
<View style={{ flex:1,flexDirection:'row',backgroundColor:'#FFF'}}>
<View style={{flexDirection:'column',margin:10}}>
{rowData.adbHomeAddress}
<CircleCheckBox
checked={rowData.checked}
onToggle={()=>this._onPressRow(rowID, rowData,rowData.checked)}
labelPosition={LABEL_POSITION.LEFT}
label={rowData.Address1 +" ,\n "+ rowData.Address2 +",\n"+rowData.ctiName+", "+rowData.staName+", "+rowData.ctrName+","+rowData.adbZip+"."}
innerColor="#C72128"
outerColor="#C72128"
styleLabel={{color:'#000',marginLeft:10}}
/>
</View>
</View>
</View>
);
}
_onPressRow = (rowID,rowData,checked) => {
const {check_data,filter} = this.state;
console.log('rowdata',rowData);
console.log('rowid',rowID);
console.log('checked',checked);
rowData.checked = !rowData.checked;
var dataClone = this.state.check_data;
dataClone[rowID] = rowData;
this.setState({check_data: dataClone });
}
}
Link to the CircleCheckBox component used: https://github.com/paramoshkinandrew/ReactNativeCircleCheckbox
I had the same requirement and wasted hours looking for solution. Eventually, I was able to resolve the problem on my own.
Posting my answer below, l have used hooks in the example, let me know if someone wants a class-based solution.
const checkboxComponent = () => {
const [checkboxValue, setCheckboxValue] = React.useState([
{ label: 'Customer', value: 'customer', checked: false },
{ label: 'Merchant', value: 'merchant', checked: false },
{ label: 'None', value: 'none', checked: false },
])
const checkboxHandler = (value, index) => {
const newValue = checkboxValue.map((checkbox, i) => {
if (i !== index)
return {
...checkbox,
checked: false,
}
if (i === index) {
const item = {
...checkbox,
checked: !checkbox.checked,
}
return item
}
return checkbox
})
setCheckboxValue(newValue)
}
return (
<View>
{checkboxValue.map((checkbox, i) => (
<View style={styles.checkboxContainer} key={i}>
<CheckBox
value={checkbox.checked}
onValueChange={(value) => checkboxHandler(value, i)}
/>
<Text style={styles.label}>{checkbox.label}</Text>
</View>
))}
</View>
)
}
export default checkboxComponent
I suggest you to use FlatList instead of ListView it's more advance and easy to use component.
For your issue please create a state checkedItem: -1 and directly assign id of your item you check last then just add a check to your CircleCheckBox item. something like below code.
<CircleCheckBox
checked={rowData.id === this.state.checkedItem}
onToggle={(rowID)=> this.setState({ checkedItem: rowID})}
labelPosition={LABEL_POSITION.LEFT}
label={rowData.Address1 +" ,\n "+ rowData.Address2 +",\n"+rowData.ctiName+", "+rowData.staName+", "+rowData.ctrName+","+rowData.adbZip+"."}
innerColor="#C72128"
outerColor="#C72128"
styleLabel={{color:'#000',marginLeft:10}}
/>
Let me know if any query.

React Native Form Validation

I created a login form using react-native and I want to validate every fields but I don't know how to do it. I'm quite new to react-native so I want to ask anyone for help. Form validation should show error under following conditions:
Input form is empty
Email text isn't email form.
Password text does not satisfy the conditions above.
If Input form has errors the login button should be disabled.
If Input form doesn't have any errors, show alert to inform login
success
Sample image validation:
Here is my code:
import React from 'react';
import { StyleSheet, Text, View, Image, TextInput, Dimensions, ScrollView,
CheckBox, TouchableOpacity } from 'react-native';
import logo from './image/Logo.png'
const { width: WIDTH } = Dimensions.get('window')
export default class App extends React.Component {
constructor(){
super();
this.state={
check:false,
email: '',
};
this.validates = this.validates.bind(this);
}
CheckBoxText(){
this.setState({
check:!this.state.check,
})
}
validates = () => {
let text = this.state.email;
let emailError = this.state.emails;
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(reg.test(text) === false)
{
console.warn("Invalid email")
this.setState({email:text})
return false;
}
else {
this.setState({email:text})
console.log("Email is Correct");
}
}
render() {
return (
<View>
<View style={styles.container}>
<Image source={logo} style={styles.logo}/>
</View>
<View style = {styles.container2}>
<Text style={styles.emailAdd}>
Email
</Text>
<TextInput
onChangeText={(text) => this.setState({email:text})}
type='email'
value={this.state.email}
keyboardType='email-address'
style={styles.emailInput}
placeholder={'Input Email Address'}
underlineColorAndroid='transparent'/>
</View>
<View style = {styles.container3}>
<Text style={styles.password}>
Password
</Text>
<TextInput
style={styles.passwordInput}
placeholder={'Input Password'}
secureTextEntry={true}
underlineColorAndroid='transparent'/>
</View>
<View style = {styles.container4}>
<View>
<CheckBox value={this.state.check} onChange={()=>this.CheckBoxText()} style={styles.rememberMe}/>
</View>
<View>
<Text style={styles.remember}>Remember me</Text>
</View>
</View>
<TouchableOpacity style={styles.btnLogin} onPress={this.validates} >
<Text style={styles.txtLogin}>Sign In</Text>
</TouchableOpacity>
</View>
);
}
}
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
if (!email) {
Toast.show('Email is required.');
} else if (!email.match(validRegex)) {
Toast.show('Invalid Email');
} else if (!password) {
Toast.show('Password is required.');
}
I recommend using formik and yup to easily build a login form with input validation. These two packages when integrated together, simplifies your codebase thanks to both of its features.
Please take a look at a CodeSandbox snippet here, https://codesandbox.io/s/stack-overflow-54204827-llvkzc?file=/index.tsx:254-656. And note, I'm using typescript here.
The package.json file at the time of written snippet is:
"dependencies": {
...
"formik": "2.2.9",
...
"yup": "0.32.11"
},
And to break the solution down, first we define our yup schema for our Login form:
Note, you may tweak the regex pattern later, as this password validation accepts min 6 to max 12 characters, with at least one uppercase letter, one lowercase letter, one number and one special character.
/**
* The `yup` Login Form schema
*/
const LoginSchemaA = Yup.object().shape({
email: Yup.string()
.email("Invalid email.")
.required("Email must be provided."),
password: Yup.string()
.required("Password must be provided.")
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##\$%\^&\*])(?=.{6,12})/,
"Password must be minimum 6 and maximum 12 characters."
)
});
Note, .email("Invalid email.") here is the default email validation feature used. You can remove this, and use .matches(...) function instead for your own regular expression.
And just the <Formik /> section for your further use:
<Formik
initialValues={{
email: "",
password: ""
}}
validationSchema={LoginSchemaA}
onSubmit={(
values: Values,
{ setSubmitting }: FormikHelpers<Values>
) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 500);
}}
>
{({ errors, touched }) => (
<Form>
<label htmlFor="email">Email</label>
<Field
id="email"
name="email"
placeholder="john.doe#email.com"
type="email"
/>
{errors.email && touched.email ? (
<div style={{ color: "red" }}>{errors.email}</div>
) : null}
<label htmlFor="password">Password</label>
<Field id="password" name="password" type="password" />
{errors.password && touched.password ? (
<div style={{ color: "red" }}>{errors.password}</div>
) : null}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
Lastly, you would want to grab the values itself for further use - ignore the setTimeout, alert and setSubmitting usages.
An example output of JSON.stringify(values, null, 2) would be as below:
{
"email": "john.doe#test.com",
"password": "Awesome#Password!2022"
}
Hope this helps you in your react-native coding journey!
here is my code you can try this
import React, { Component } from "react"
import { View, Button } from "react-native"
import TextField from "textfield"
import validation from "validation"
import validate from "validation_wrapper"
export default class Form extends Component {
constructor(props) {
super(props)
this.state = {
email: "",
emailError: "",
password: "",
passwordError: ""
}
}
register() {
const emailError = validate("email", this.state.email)
const passwordError = validate("password", this.state.password)
this.setState({
emailError: emailError,
passwordError: passwordError
})
if (!emailError && !passwordError) {
alert("Details are valid!")
}
}
render() {
return (
<View>
<TextField
onChangeText={(value) => this.setState({ email: value.trim() })}
onBlur={() => {
this.setState({
emailError: validate("email", this.state.email)
})
}}
error={this.state.emailError}
/>
<TextField
onChangeText={(value) => this.setState({ password: value.trim() })}
onBlur={() => {
this.setState({
passwordError: validate("password", this.state.password)
})
}}
error={this.state.passwordError}
secureTextEntry={true}
/>
<Button title="Register" onPress={this.validateRegister} />
</View>
)
}
}
<!-- begin snippet: js hide: false console: true babel: false -->
const validation = {
email: {
presence: {
message: "^Please enter an email address"
},
email: {
message: "^Please enter a valid email address"
}
},
password: {
presence: {
message: "^Please enter a password"
},
length: {
minimum: 5,
message: "^Your password must be at least 5 characters"
}
}
}
export default validation
import validation from "validation.js"
export default function validate(fieldName, value) {
// Validate.js validates your values as an object
// e.g. var form = {email: 'email#example.com'}
// Line 8-9 creates an object based on the field name and field value
var formValues = {}
formValues[fieldName] = value
// Line 13-14 creates an temporary form with the validation fields
// e.g. var formFields = {
// email: {
// presence: {
// message: 'Email is blank'
// }
// }
var formFields = {}
formFields[fieldName] = validation[field]
// The formValues and validated against the formFields
// the variable result hold the error messages of the field
const result = validatejs(formValues, formFields)
// If there is an error message, return it!
if (result) {
// Return only the field error message if there are multiple
return result[field][0]
}
return null
}
import React from "react"
import { View, TextInput, Text } from "react-native"
const TextField = (props) => (
<View>
<TextInput />
props.error ? <Text>{props.error}</Text> : null
</View>
)
export default TextField

Unhandled Promise Rejection : Existing value for key "Favorites" must be of type null or array, revived string

my problem is quite simple but I'm new to react native dev. I'd like to save multiple elements with AsyncStorage (I'm using react-native-simple-store
a library that works like a wrapper but it's same logic) I want display all items for a key in a list , my code look like this:
constructor(props) {
super(props)
this.state = {
UserInput: "",
}
}
SaveValue = () => {
store.push('Favorites', this.state.UserInput)
Keyboard.dismiss()
};
FetchValue = () => {
store.get('Favorites').then((value) => {
this.setState({
favs: value
});
}).done();
};
Same thing with AsynStorage, it just update the item which is not my goal, I'd like to add a new one
SaveValue = () => {
AsyncStorage.setItem("Favorites", this.state.UserInput);
Keyboard.dismiss()
};
FetchValue = () => {
AsyncStorage.getItem("Favorites").then((value) => {
this.setState({
favs: value
});
}).done();
};
This part is my view where I try to display data, you can also see that I use a text input and two buttons one to save and the other to display an array of items stored
render() {
return (
<View>
<TextInput
onChangeText={(UserInput) => this.setState({UserInput})}
placeholder= "Type something"
value={this.state.UserInput} />
<TouchableOpacity
onPress={this.SaveValue}>
<Text>Save</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.FetchValue}>
<Text>Fetch</Text>
</TouchableOpacity>
<Text>{this.state.favs}</Text>
</View>
);
}
At this point I can see only one item, I tried to figure it out and saw that I have to use another method called push but when I changed save by push it throw me an error
Unhandled Promise Rejection : Existing value for key "Favorites" must be of type null or array, revived string.
Thanks!
it will work :)
renderFavorites = () => {
AsyncStorage.getItem("Favorites").then((favs) => {
favs.map((fav) => {
return (<Text> {fav} </Text>);
});
});
}
render() {
return (
<View>
<TextInput
onChangeText={(UserInput) => this.setState({UserInput})}
placeholder= "Type something"
value={this.state.UserInput} />
<TouchableOpacity
onPress={this.SaveValue}>
<Text>Save</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.FetchValue}>
<Text>Fetch</Text>
</TouchableOpacity>
{this.renderFavorites()}
</View>
);
}
Solution using JSON:
SaveValue = () => {
const newFavs = [...this.state.favs, this.state.UserInput];
this.setState({ favs: newFavs, UserInput: '' }, () => {
AsyncStorage.setItem("Favorites", JSON.stringify(this.state.favs));
Keyboard.dismiss()
});
};
FetchValue = () => {
AsyncStorage.getItem("Favorites").then((value) => {
this.setState({
favs: JSON.parse(value)
});
}).done();
};