React-Native Picker with Formik - javascript

My basic forms work fine, however, as there are some caveats with react-native, I could use some help in knowing if the issue is how I am using formik, or using it with react-native.
In this particular form, when I attempt to fill in a picker in react-native using formik, the form resets the picker to the original value immediately after I select an option. I have stripped the code down, as I feel someone should have the answer without a lot of code, but I am not seeing what I am missing. Thanks.
<Formik
onSubmit={
props.onSubmit(props.values)
}
mapPropsToValues = {(props) => ({
id: props.id,
status: props.status
})}
validate={values => {
// same as above
let errors = {};
return errors;
}}
onValueChange={ (itemIndex) => {
this.props.values.status = itemIndex
}}
render= { props => (
const { id, status } = this.props
<View>
<Text style={styles.textResultsHeaderStyle}>Job: {id}</Text>
<Picker
selectedValue={status}
onValueChange={itemIndex => this.onValueChange}>
<Picker.Item label="New" value="0" />
<Picker.Item label="Requested" value="1" />
<Picker.Item label="Responded" value="2" />
<Picker.Item label="Closed" value="3" />
</Picker>
<RoundedButton disabled={props.isSubmitting} onPress={props.handleSubmit} text="SUBMIT" />
</View>
)}
/>

I just answered a similar question on github. I suppose you're using the built in picker component in RN. If not then you need to check the documentation for your picker component to see how to get the value on change.
https://github.com/jaredpalmer/formik/issues/1378#issuecomment-480189488

Related

React native paper TextInput in Modal, cursor flashes backwards after a character is input

Some odd behaviour on a TextInput on a Modal in React Native Paper. When I type a character, it is input into the text box, but then the cursor flashes back (as if it is deleted) and then it reappears again. This all happens very quickly and the character is retained, but it all looks a bit janky. Gif below to illustrate:
The code is fairly simple for the modal:
import { Portal, Modal, Button, Title, Text, TextInput } from 'react-native-paper';
const [nameNew, setNameNew] = useState('')
const [emailNew, setEmailNew] = useState('')
const renderModalAddPerson = () => {
return (
<Portal>
<Modal visible={visibleModalAddPerson} onDismiss={closeModalAddPerson} contentContainerStyle={styles.modalContainer}>
<View>
<Title style={{alignSelf:'center'}}>Title here</Title>
<Text> </Text>
<TextInput
mode="outlined"
label="Name"
style={{alignSelf:'center', width:'95%'}}
value={nameNew}
onChangeText={nameNew => setNameNew(nameNew)}
ref={input1}
returnKeyType='next'
blurOnSubmit={false}
onSubmitEditing={() => input2.current.focus()}
/>
<TextInput
mode="outlined"
label="Email"
style={{alignSelf:'center', width:'95%'}}
value={emailNew}
onChangeText={emailNew => setEmailNew(emailNew)}
ref={input2}
returnKeyType='done'
blurOnSubmit={false}
onSubmitEditing={() => addPerson()}
/>
<Button
uppercase={false}
style={{backgroundColor:'#2c3e50', width: '95%', alignSelf:'center', margin: 10}}
labelStyle={{color:'white'}}
onPress={()=>addPerson()}
>Add person</Button>
</View>
</Modal>
</Portal>
);
};
Issue observed on iOS and not tested on Android
Looks like this is a known bug in React Native. Best solution I have found is to use defaultValue prop instead of value.
The only thing I can see is that your using the same variable name to update your state as the name of the state which could be causing something weird to happen.
<TextInput
mode="outlined"
label="Name"
style={{alignSelf:'center', width:'95%'}}
value={nameNew}
onChangeText={val => setNameNew(val)}
ref={input1}
returnKeyType='next'
blurOnSubmit={false}
onSubmitEditing={() => input2.current.focus()}
/>
Please try the above as I've tested and its working as expected for me.

React Native FlatList only gets updated when TextInput or Picker changes

I'm trying to dynamically add text from an input field and a picker to a FlatList in React Native. The problem I have is that the FlatList doesn't get updated as soon as I hit the button.
The item does get added to the list eventually but only after I trigger the onChangeText of the text input or the onValueChange of the picker element.
import React, { Component } from 'react';
import { View, Text, Picker } from 'react-native';
import { TextInput } from 'react-native-paper';
import { TouchableOpacity, FlatList, ScrollView } from 'react-native-gesture-handler';
CreateSetScreen = () => {
const [value, onChangeText] = React.useState('Placeholder');
const [pickerValue, setPickerValue] = React.useState("1");
const [listValues, setListValue] = React.useState([]);
joinData = () => {
listValues.push({"content": value, "category": pickerValue, "key": listValues.length.toString()});
setListValue(listValues);
}
return(
<View>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={text => onChangeText(text)}
value={value}
/>
<Picker
selectedValue={pickerValue}
style={{height: 50}}
onValueChange={(itemValue) => setPickerValue(itemValue)}
>
<Picker.Item label="Kategorie 1 - 1%" value="1" />
<Picker.Item label="Kategorie 2 - 10%" value="2" />
<Picker.Item label="Kategorie 3 - 20%" value="3" />
<Picker.Item label="Kategorie 4 - 29%" value="4" />
<Picker.Item label="Kategorie 5 - 40%" value="5" />
</Picker>
<TouchableOpacity
style={{backgroundColor:'#DDDDDD'}}
onPress={() => joinData()}
>
<Text>Add Item!</Text>
</TouchableOpacity>
<FlatList
data={listValues}
renderItem={({item}) => (<Text>{item.category}: {item.content}</Text>)}
/>
</View>
)
}
export default CreateSetScreen;
Any help is appreciated, thank you.
It seems .push() works in the setter function from React Hooks but It doesn't. Rather than returning the array itself, .push() returns the length of the array after modification.
On the other hand, .concat() works to update state, that is .concat() creates a new array and then returns the changed array.
So just change your joinData function a little bit in this way.
const joinData = () => {
setListValue(listValues => listValues.concat({ "content": value, "category": pickerValue, "key": listValues.length.toString() }));
}
Try adding a key extractor to your FlatList:
<FlatList
data={listValues}
renderItem={({item}) => (<Text>{item.category}: {item.content}</Text>)}
keyExtractor={(item, index) => String(index)}
/>

onPress on picker.item to close the dropdown- react-native

I have a dropdown and I'm using react-native picker component. Everything is working fine, the problem is that I need to close the dropdown when the user presses on any picker items
this.state.list.map((obj, index) => {
return (
<Picker.Item key={index} label={obj.label} value={obj.value} />
);
the picker gives us only onValueChange prop, but I need onPress functionality for any picker items individually to close the dropdown.
I have also tried this
this.state.list.map((obj, index) => {
return (
<TouchableWithoutFeedback onPress={this.itemPressHandler}>
<Picker.Item key={index} label={obj.label} value={obj.value} />
</TouchableWithoutFeedback>
);
but it doesn't render the dropdown.
Is there any way to get this functionality?
According to docs.
<Picker
selectedValue={this.state.valueSelected}
onValueChange={(itemValue, itemIndex) => {
this.handlePickerValueChange(itemValue, itemIndex)
}
mode={'dialog'} // optional
>
this.state.list.map((obj, index) => {
return (
<Picker.Item key={index} label={obj.label} value={obj.value} />
);
});
</Picker>
Set the default selected value in state
state={
valueSelected: "Choose a value"
}
Handle change when user selects a different option
handlePickerValueChange = (itemValue, itemIndex) => {
//do your stuff
}

Use react-input-mask with antd without getFieldDecorator

I'm new to React and I would like to use react-input-mask with antd input field. If I use getFieldDecorator and Form.create, everything works.
In inputmask.js:
const CustomInput = forwardRef((props, ref) => {
return (
<ReactInputMask {...props}>
{inputProps => (
<Input
{...inputProps}
ref={ref}
disabled={props.disabled ? props.disabled : null}
/>
)}
</ReactInputMask>
);
});
CustomInput.propTypes = {
mask: PropTypes.string,
maskChar: PropTypes.string,
formatChars: PropTypes.object,
alwaysShowMask: PropTypes.bool,
inputRef: PropTypes.func
};
export default CustomInput;
In index.js:
function FormBuilder(props) {
const { getFieldDecorator } = props.form;
return (
<Form>
<Form.Item label="Phone">
{getFieldDecorator("phone", {
rules: [{ required: true, message: "Phone is required." }]
})(
<CustomInput
mask="+7 (999) 999-99-99"
placeholder="+7 (___) ___-__-__"
/>
)}
</Form.Item>
<Button htmlType="submit" type="primary">
Send
</Button>
</Form>
);
}
const App = Form.create({ name: "form" })(FormBuilder);
Here is demo.
But if I delete getFieldDecorator and Form.create (as I will use react-final-form and I won't need them), the mask doesn't work.
Here are my new index.js and demo.
function App(props) {
return (
<Form>
<Form.Item label="Phone">
<CustomInput
mask="+7 (999) 999-99-99"
placeholder="+7 (___) ___-__-__"
/>
</Form.Item>
<Button htmlType="submit" type="primary">
Send
</Button>
</Form>
);
}
The code is basically the same, but in the second example, the mask is not working. Could you please tell me, where is my mistake?
getFieldDecorator API manages it's form fields values for you.
In case you remove it, you need to manage it by yourself.
You can assure it by rendering only CustomInput and it still won't work as you were aspected.
function FormBuilder(props) {
// Won't work
return (
<CustomInput mask="+7 (999) 999-99-99" placeholder="+7 (___) ___-__-__" />
);
}
To fix it, as mentioned above, manage the input state by yourself, for example:
function App(props) {
const [value, setValue] = useState('');
return (
<Form>
<Form.Item label="Phone">
<CustomInput
value={value}
onChange={e => setValue(e.target.value)}
mask="+7 (999) 999-99-99"
placeholder="+7 (___) ___-__-__"
/>
</Form.Item>
<Button htmlType="submit" type="primary">
Send
</Button>
</Form>
);
}
Notes:
if you using other form library like react-final-form don't mix components, like using Form.Item of antd inside Form of
react-final-form, use a single library or you will get
unexpected results.
antd Form is an implementation of rc-form so you can use its extended api.
When you using a complete UI-library like antd you need a very good reason to use other components, rethink if you really need
other form library.

How to make react-native Picker stay at newly selected option?

I have a picker that I'm testing on iOS right now with two options. Every time I drag down from the first option to the second option, the picker immediately returns to the first option.
This is what my the code for my picker looks like.
<Picker
style={{
width: 100,
}}
selectedValue={(this.state && this.state.pickerValue) || 'a'}
onValueChange={(value) => {
this.setState({value});
}} itemStyle={{color: 'white'}}>
<Picker.Item label={'Hello'} value={'a'} />
<Picker.Item label={'World'} value={'b'} />
</Picker>
I want the selector to stay at the newly scrolled-to option. I've also removed the || 'a' part of the selectedValue attribute but that didn't solve the issue either.
On value change you need to specify which property of the state changed and change it accordingly with this.setState
onValueChange={(value) => {this.setState({pickerValue: value});
Complete Code
<Picker
style={{
width: 100,
}}
selectedValue={(this.state && this.state.pickerValue) || 'a'}
onValueChange={(value) => {
this.setState({pickerValue: value});
}} itemStyle={{color: 'white'}}>
<Picker.Item label={'Hello'} value={'a'} />
<Picker.Item label={'World'} value={'b'} />
</Picker>
I just came across this and was facing the same issue, the scrolling reaches the new item and resets to the first item.
I have done this using stateless component (Hooks):
I have an array of objects as the value and option as key
const data = useState({
"options":[{
"name":"Dish 1","price":0},{"name":"Dish 2","price":0}]})
const [selected, setSelected] = useState(0)
The Picker component:
<PickerIOS
selectedValue={selected_choice}
onValueChange={(value, index) => {
set_selected_choice(index)
}}
>
{data?.map((item, index) => (
<PickerIOS.Item
key={item}
value={index}
label={item.name}
/>
))}
</PickerIOS>
Here, I have stored the index of the array elements in the selected state and have updated it from the PickerIOS Item, keeping the value as index.
I used this "hack":
render() {
const values = ['1', '2'];
return (
<Picker
value={this.state.value}
onValueChange={this.onValueChange.bind(this)}
>
{
<Picker
value={this.state.value}
onValueChange={this.onValueChange.bind(this)}
>
{
[<Picker.Item
label="n/a"
value={null}
/>].concat(values.map(value => {
return (
<Picker.Item
label={value}
value={value}
/>
)
})
)
}
</Picker>
);
}

Categories

Resources