How can i use Dropdown picker setValue with Formik? handleChange or setValueField doesn't work properly. Any suggestions?
const [open, setOpen] = useState(false);
const items = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
];
return (
<Formik
initialValues={{ fruits: [] }}
onSubmit={values => console.log(values)}
>
{({ handleChange, handleSubmit, values }) => (
<View>
<DropdownPicker
schema={{ label: 'label', value: 'label' }}
open={open}
setOpen={setOpen}
value={values.fruits}
setValue={handleChange('fruits')}
items={items}
multiple
/>
<Button onPress={handleSubmit} title="Submit" />
</View>
)}
</Formik>
);
};```
You could wrap DropdownPicker in another component and use useFormikContext and useField formik hooks to be able to use it, take a look in this example:
import { useFormikContext, useField } from "formik";
import React, { useState } from "react";
import DropDownPicker from "react-native-dropdown-picker";
const DropDownFormik = ({ ...props }) => {
const [openProvider, setOpenProvider] = useState(false);
const { setFieldValue } = useFormikContext();
const [field] = useField(props);
return (
<DropDownPicker
{...field}
{...props}
value={field.value}
open={openProvider}
setOpen={setOpenProvider}
setValue={(val) => {
setFieldValue(field.name, val());
}}
/>
);
};
export default DropDownFormik;
to use this component just specify the name in your form:
<Formik
initialValues={{ level: ""}}
onSubmit={(values) => console.log(values)}
>
{({ handleChange, handleBlur, handleSubmit, values }) => (
<View>
<DropDownFormik
zIndex={3000}
name="level"
placeholder="Notification Level"
items={notificationLevels}
/>
</View>
)}
</Formik>
Related
I saw same question a couple of times but none of the answers made sense or solved it so maybe someone could help! I want to replace formiks onchange function with my own custom usestate hook. However, when I do that none of the validations work anymore, strangely none accept the error that field is required stays. Anyone know how to do it? Thanks!!
const DisplayingErrorMessagesSchema = Yup.object().shape({
password: Yup.string()
.min(2, 'Too Short!')
.max(50, 'Too Long!')
.required('Required'),
email: Yup
.string()
.email('Invalid email')
.required('Required')
.min(2, ({ min }) => `Password must be at least ${min} characters`)
});
const App = () => {
const { formData, handleInputChange } = useForm(
{
email: "",
password: ""
}
);
return (
<>
<Formik
initialValues={formData}
validationSchema={DisplayingErrorMessagesSchema}
onSubmit={values => console.log(values)}
>
{({ handleSubmit, isValid, values }) => (
<>
<Field
component={CustomInput}
name="title"
placeholder="Title"
/>
<Field
component={CustomInput}
name="post"
placeholder="Write post..."
multiline
numberOfLines={3}
/>
<Button
onPress={handleSubmit}
title="POST"
disabled={!isValid}
/>
</>
)}
</Formik>
</>
)
}
const CustomInput = (props) => {
const {
field: { name, onBlur, onChange, value },
form: { errors, touched, setFieldTouched },
...inputProps
} = props
const hasError = errors[name] && touched[name]
return (
<>
<TextInput
style={[
styles.textInput,
hasError && styles.errorInput
]}
value={value}
onChangeText={(text) => onChange(name)(text)}
onBlur={() => {
setFieldTouched(name)
onBlur(name)
}}
{...inputProps}
/>
{hasError && <Text style={styles.errorText}>{errors[name]}</Text>}
</>
)
}
I create a component as below.
It allows the user to input their id and submits it.
Now I want to clear the input value after submit.
How to implement?
const InputUserModal = ({ onConfirm }) => {
const { register, control, handleSubmit, errors } = useForm();
useEffect(() => {
register({ name: 'userId' }, { required: true });
}, [register]);
return (
<View>
<Controller
control={control}
name="userId"
defaultValue=""
render={({ onChange, value }) => (
<LabelInput
label='user'
iconSetting={{
name: 'user',
type: 'feather',
}}
onSubmitEditing={handleSubmit(onConfirm)}
autoFocus
onChangeText={v => onChange(v)}
value={value}
/>
)}
/>
</View>
);
};
As you're using Formik, It provides reset function, which you can pass as a reference in onConfirm as below:
const InputUserModal = ({ onConfirm }) => {
const { register, control, handleSubmit, errors,resetForm } = useForm();
useEffect(() => {
register({ name: 'userId' }, { required: true });
}, [register]);
return (
<View>
<Controller
control={control}
name="userId"
defaultValue=""
render={({ onChange, value }) => (
<LabelInput
label='user'
iconSetting={{
name: 'user',
type: 'feather',
}}
onSubmitEditing={handleSubmit(onConfirm(values,resetForm)}
autoFocus
onChangeText={v => onChange(v)}
value={value}
/>
)}
/>
</View>
);
};
Assume that you render this component as below
const ParentComponent = ()=>{
const handleOnConfirm =(values,resetForm)=>{
// Your function to submit input values to a server
// then resetFormik Values
resetForm()
}
return (
<InputUserModal onConfirm={handleOnConfirm}/>
)
}
I'm new to Formik and React Native. Currently, my goal is to submit a form via the Navigation header. All the examples I found are to submit the form from the screen page itself. I need help figure out how to properly use the handleSubmit and onSubmit property and setParams to pass the value to the navigation header properly.
Right now, I'm stuck by not sending the values from the form to the useCallBack hook.
import React, {useState, useEffect, useCallback} from 'react';
import {StyleSheet, View, Button, Text} from 'react-native';
import {Input} from 'react-native-elements';
import {useDispatch} from 'react-redux';
import Colors from '../../constants/Colors';
import {
HeaderButtons,
HeaderButton,
Item,
} from 'react-navigation-header-buttons';
import {Ionicons} from '#expo/vector-icons';
import {CommonActions} from '#react-navigation/native';
import * as prodActions from '../../store/actions/products';
import {Formik} from 'formik';
import * as Yup from 'yup';
const AddProduct = ({navigation}) => {
const dispatch = useDispatch();
const submitHandler = useCallback(() => {
dispatch(prodActions.addProduct(value));
}, [value]);
useEffect(() => {
navigation.dispatch(CommonActions.setParams({submitForm: submitHandler}));
}, [submitHandler]);
return (
<View style={styles.screen}>
<Formik
initialValues={{title: '', description: '', imageUrl: ''}}
validationSchema={Yup.object({
title: Yup.string().required('please input your title'),
description: Yup.string().required('please input your description'),
imageUrl: Yup.string()
//.email('Please input a valid email.')
.required('Please input an email address.'),
})}
onSubmit={submitHandler}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
touched,
errors,
}) => (
<View>
<Input
label="Title"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('title')}
onBlur={handleBlur('title')}
value={values.title}
// errorMessage={errors.title}
/>
{touched.title && errors.title ? (
<Text style={styles.error}>{errors.title}</Text>
) : null}
<Input
label="Description"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('description')}
onBlur={handleBlur('description')}
value={values.description}
/>
{touched.description && errors.description ? (
<Text style={styles.error}>{errors.description}</Text>
) : null}
<Input
label="Image URL"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('imageUrl')}
onBlur={handleBlur('imageUrl')}
value={values.imageUrl}
/>
{touched.imageUrl && errors.imageUrl ? (
<Text style={styles.error}>{errors.imageUrl}</Text>
) : null}
<Button onPress={handleSubmit} title="Submit" />
</View>
)}
</Formik>
</View>
);
};
const IoniconsHeaderButton = (props) => (
<HeaderButton
IconComponent={Ionicons}
iconSize={23}
color="white"
{...props}
/>
);
export const AddProductHeaderOptions = (navData) => {
const updateForm = navData.route.params.submitForm;
return {
headerRight: () => {
return (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item title="add" iconName="save-outline" onPress={updateForm} />
</HeaderButtons>
);
},
};
};
export default AddProduct;
const styles = StyleSheet.create({
screen: {
flex: 1,
marginHorizontal: 20,
marginTop: 20,
},
error: {
marginLeft: 8,
fontSize: 14,
color: 'red',
},
});
You're probably using "value" variable with an empty value. You must ref all your form data, inside your < Formik > , let's use street, for an example:
value={values.street}
error={touched.street && !isSubmitting && errors.street}
returnKeyType="next"
onSubmitEditing={() => refs.number.current?.focus()}
ref={refs.street}
So declare this ref variable first:
const refs = {
street: useRef<any>(null),
number: useRef<any>(null),
zipCode: useRef<any>(null),
//....
If you don't wanna go with REF, so at least declare the variable and try it, like that:
const [value, setValue] = useState();
Also, the VALUE name is not a good variable name because there are a lot of native things using it. But, considering that you must have taken this from an example, use useState with your form or object and you should be good.
const [formx, setFormx] = useState();
For those who use React-Navigation 5 and Formik 1.5.x+ Here is a good solution.
Declare a ref variable outside/inside of your function component to later Attach to your
let formRef;//or you can use const formRef = useRef(); inside function component
If you declare formRef outside your function component use React hook {useRef} to update the ref
formRef = useRef();
Render
<Formik innerRef={formRef} />
And finally in your header button call this
navigation.setOptions({
headerRight: () => (
<Button onPress={() => {
if (formRef.current) {
formRef.current.handleSubmit()
}
}}/>
)
});
Another solution, which I think could be better is to use the useFormik, which gives you access to all the form handlers and form meta:
const {
handleSubmit,
handleChange,
isValid,
} = useFormik({
initialValues: {...},
validationSchema: yupValidationSchema,
onSubmit: (formValues) => {
//save here...
}
});
Then you can simply pass the handleSubmit reference to your right header navigation using the useLayoutEffect as proposed in the official documentation:
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => {
return (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item title="add" iconName="save-outline" onPress={handleSubmit}/>
</HeaderButtons>
);
}
});
});
This approach is clear in my opinion, since you don't need to handle all the structural disadvantages of using the renderProps approach with
<Formkik>
{({ handleSubmit }) => (
jsx here...
)}
</Formik>
I'm using using this data to fill a FlatList, each item contains a LinearGradient
const DATA = [
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
firstColor: "#f472a7",
secondColor: "#d84351"
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
firstColor: "#50be71",
secondColor: "#50be71"
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
firstColor: "#e2bd4f",
secondColor: "#e49074"
}
];
I added two properties called "firstColor" and "secondColor", to fill the LinearGradient colors, but I'm having some issues doing that. I'm receiving this error:
TypeError: undefined is not an object (evaluating '_ref3.secondColor')
Code:
const Item = ({ title }, { firstColor }, { secondColor }) => (
<LinearGradient
colors={[{firstColor}, {secondColor} ]}
style={styles.item}
>
<Text style={styles.title}>{title}</Text>
</LinearGradient>
);
const renderItem = ({ item }) => (
<Item title={item.title} />
);
...
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
Try this
const Item = ({ title, firstColor, secondColor}) => (
<LinearGradient
colors={[firstColor, secondColor ]}
style={styles.item}
>
<Text style={styles.title}>{title}</Text>
</LinearGradient>
);
const renderItem = ({ item}) => (
<Item
firstColor={item.firstColor }
secondColor={ item.secondColor }
title={item.title}
/>
);
const LinearGradient = ({ color, firstColor, secondColor }) => {
const colorArr = [firstColor, secondColor];
return (
<View>
<Text>{color}</Text>
<Text>{firstColor}</Text>
<Text>{secondColor}</Text>
</View>
);
};
can you try this, you were passing the data in the wrong way. I did some changes in similar example.
I have a question about how to pass props for one screen to another, and display the props using listItem from "React Native Elements".
First i will paste the code of the screen with the "Add Friend" form:
const addFriend = ({ navigation }) => {
const [friend, setFriend] = useState('');
const [phone, setPhone] = useState('');
return (
<View style={styles.container}>
<Input
placeholder='Friend Name'
onChangeText={friend => setFriend(friend)}
leftIcon={
<Icon
name='user'
size={24}
color='grey'
/>
}
/>
<Input
onChangeText={phone => setPhone(phone)}
placeholder='Friend Phone Number'
leftIcon={
<Icon
name='phone'
size={24}
color='grey'
/>
}
/>
<Button
title="Add Friend"
onPress={() => {
navigation.navigate('FriendList', { friend, phone })
}}
/>
</View>
);
}
Second, i will paste the screen that is suppose to show the list of friends i want to add, here i cant find the way to pass the props from the screen above, to the list:
const list = [
{
name: '',
avatar_url: 'https://s3.amazonaws.com/uifaces/faces/twitter/ladylexy/128.jpg',
subtitle: ''
}
]
const FriendList = ({ route, navigation }) => {
const { friend } = route.params;
const { phone } = route.params;
return (
<View style={styles.container}>
list.map((l, i) => (
<ListItem
key={i}
leftAvatar={{ source: { uri: l.avatar_url } }}
title={l.name}
subtitle={l.subtitle}
bottomDivider
/>
))
}
{/* <Text>{JSON.stringify(friend)} {JSON.stringify(phone)}</Text> */}
</View>
);
}
You can find your parameters that you pass to your second screen in
let {friend, phone} = this.props.navigation.state.params;