Enzyme won't detect components from React Native Elements - javascript

I am testing my component using Jest and Enzyme which has Input component from React Native Elements but I am getting error
Method “simulate” is meant to be run on 1 node. 0 found instead
Here is my code:
import 'react-native';
import React from 'react';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';
import TransactionDetails from '../../app/BankingFeatures/components/transactionDetails';
const userProfile = {
data: {
user_profile: {
name: 'ashwith',
payment_status_id: '1234556'
}
}
};
const state = {
name: 'ashwith',
amount: null,
transaction_id: null,
transaction_date: '2019/06/18',
transaction_type: null,
name_status: undefined,
amount_status: undefined,
transaction_id_status: undefined,
transaction_date_status: undefined,
transaction_type_status: undefined,
};
describe('Transaction Test', () => {
const wrapper = shallow(<TransactionDetails
userProfile={userProfile}
/>);
const input = wrapper.find('Input').at(0);
input.simulate('changeText', 'eths');
});
transactionDetails.js
import moment from 'moment';
import React, { Component } from 'react';
import DatePicker from 'react-native-datepicker';
import Spinner from 'react-native-loading-spinner-overlay';
import { Icon, Input, Button } from 'react-native-elements';
import {
View,
Text,
ScrollView,
Platform,
KeyboardAvoidingView,
} from 'react-native';
import { Colors } from '../../Metrics';
import { styles } from '../styles/transactionDetails';
import MainContainer from '../../Utilities/mainContainer';
import * as Errmsg from '../../../config/Contents/errMessages';
import * as Placeholdermsg from '../../../config/Contents/placeHolders';
class TransactionDetails extends Component {
// State
constructor(props) {
super(props);
const { userProfile } = this.props;
const userData = userProfile && userProfile.data && userProfile.data.user_profile;
const dateNow = moment(new Date()).format('YYYY/MM/DD');
this.state = {
name: userData ? userData.name : null,
amount: null,
transaction_id: null,
transaction_date: dateNow,
transaction_type: null,
name_status: undefined,
amount_status: undefined,
transaction_id_status: undefined,
transaction_date_status: undefined,
transaction_type_status: undefined,
};
}
getState = () => this.state;
change = (transaction_id, amount) => this.setState({ transaction_id, amount });
// Validating Transaction
async nameValidation() {
const { name } = this.state;
const name_status = name ? undefined : Errmsg.name;
if (name_status === undefined) {
// this.tranIdInput.focus();
} else {
await this.setState({
name_status
});
}
}
// Validating Transaction Date
async transactionDateValidation() {
const { transaction_date } = this.state;
const transaction_date_status = await transaction_date ? undefined : 'Transaction date required';
if (transaction_date_status === undefined) {
this.tranIdInput.focus();
} else {
await this.setState({
transaction_date_status
});
}
}
// Validating Transaction ID
async transIdValidation() {
const { transaction_id } = this.state;
const transaction_id_status = transaction_id ? undefined : 'Transaction id required';
if (transaction_id_status === undefined) {
// this.transTypenput.focus();
} else {
await this.setState({
transaction_id_status
});
}
}
// Validating Transaction Type
async transTypeValidation() {
const { transaction_type } = this.state;
const transaction_type_status = transaction_type ? undefined : 'Transaction type required';
if (transaction_type_status === undefined) {
this.amountInput.focus();
} else {
await this.setState({
transaction_type_status
});
}
}
// Validating Amount
async amountValidation() {
const { amount } = this.state;
const amount_status = !isNaN(amount) ? amount && amount.length >= 5 ? undefined : 'min you can add 10000' : 'Amount required';
if (amount_status === undefined) {
// this.btnPress();
} else {
await this.setState({
amount_status
});
}
}
// Submitting Transaction details
btnPress() {
const { actions } = this.props;
const {
name,
amount,
transaction_id,
transaction_date,
transaction_type,
} = this.state;
actions.addBankTransactionDetails({
name,
amount,
transaction_id,
transaction_date,
transaction_type,
});
}
render() {
const {
name_status,
amount_status,
transaction_id_status,
transaction_date_status,
transaction_type_status,
} = this.state;
const { addBankTransactionDetails } = this.props;
return (
<MainContainer style={styles.container}>
<Spinner
cancelable
visible={addBankTransactionDetails && addBankTransactionDetails.isLoading}
textContent={'Loading...'}
/>
<KeyboardAvoidingView style={styles.container} behavior={Platform.OS === 'ios' && 'height'}>
<ScrollView
bounces={false}
showsVerticalScrollIndicator={false}
>
<View style={styles.formContainer}>
<Text style={styles.title}>Add Transaction Details</Text>
<Input
placeholder={Placeholdermsg.name}
ref={this.nameInput}
leftIcon={
<Icon
name='user'
size={24}
color={Colors.grey}
type='font-awesome'
/>
}
autoFocus
value={this.state.name}
autoCapitalize='characters'
returnKeyType='next'
onSubmitEditing={() => { this.nameValidation(); }}
onChangeText={(name) => this.setState({ name, name_status: undefined })}
inputStyle={styles.textinput}
containerStyle={styles.textFeildContainer}
inputContainerStyle={styles.textFeild}
errorMessage={(name_status) || null}
/>
<DatePicker
style={styles.dateContainer}
date={this.state.transaction_date}
mode="date"
placeholder="Select transaction date"
format="YYYY/MM/DD"
minDate="2019-01-01"
confirmBtnText="Confirm"
cancelBtnText="Cancel"
iconComponent={
<Icon
name="calendar"
type="entypo"
size={24}
color={Colors.grey}
containerStyle={{
position: 'absolute',
left: 10,
top: 8,
marginLeft: 0
}}
/>
}
customStyles={{
dateIcon: {
position: 'absolute',
left: 0,
top: 4,
marginLeft: 0
},
dateInput: {
height: 50,
marginLeft: 50,
borderWidth: 0,
alignItems: 'flex-start',
}
}}
onCloseModal={() => this.transactionDateValidation()}
onDateChange={async (transaction_date) => { this.setState({ transaction_date, transaction_date_status: undefined }, () => { console.log(transaction_date); }); this.transactionDateValidation(); }}
/>
{
(transaction_date_status !== undefined) &&
<View style={styles.errorContainer}>
<Text style={styles.errText}>{transaction_date_status}</Text>
</View>
}
<Input
ref={(input) => { this.tranIdInput = input; }}
placeholder='Enter transaction id'
leftIcon={
<Icon
name='key'
size={24}
color={Colors.grey}
type='entypo'
/>
}
autoCapitalize='characters'
returnKeyType='next'
onSubmitEditing={() => { this.transIdValidation(); }}
onChangeText={(transaction_id) => this.setState({ transaction_id, transaction_id_status: undefined })}
inputStyle={styles.textinput}
containerStyle={styles.textFeildContainer}
inputContainerStyle={styles.textFeild}
errorMessage={(transaction_id_status) || null}
/>
<Input
ref={(input) => { this.transTypenput = input; }}
placeholder='Enter transaction type'
leftIcon={
<Icon
name='bank'
size={20}
color={Colors.grey}
type='font-awesome'
/>
}
autoCapitalize='characters'
returnKeyType='next'
onSubmitEditing={() => { this.transTypeValidation(); }}
onChangeText={(transaction_type) => this.setState({ transaction_type, transaction_type_status: undefined })}
inputStyle={styles.textinput}
containerStyle={styles.textFeildContainer}
inputContainerStyle={styles.textFeild}
errorMessage={(transaction_type_status) || null}
/>
<Input
ref={(input) => { this.amountInput = input; }}
placeholder='Enter Amount'
leftIcon={
<Icon
name='rupee'
size={24}
color={Colors.grey}
type='font-awesome'
/>
}
keyboardType='numeric'
returnKeyType='done'
onSubmitEditing={() => { this.amountValidation(); }}
onChangeText={(amount) => this.setState({ amount, amount_status: undefined })}
inputStyle={styles.textinput}
containerStyle={styles.textFeildContainer}
inputContainerStyle={styles.textFeild}
errorMessage={(amount_status) || null}
/>
</View>
</ScrollView>
</KeyboardAvoidingView>
<View style={styles.bottomContainer}>
<Button
title="Submit"
buttonStyle={styles.Button}
onPress={() => this.btnPress()}
containerStyle={{ alignSelf: 'stretch' }}
/>
</View>
</MainContainer>
);
}
}
export default TransactionDetails;
debug
<MainContainer style={{...}}>
<Spinner cancelable={true} visible={false} textContent="Loading..." animation="none" color="white" size="large" overlayColor="rgba(0, 0, 0, 0.25)" />
<KeyboardAvoidingView style={{...}} behavior="height" enabled={true} keyboardVerticalOffset={0}>
<ScrollViewMock bounces={false} showsVerticalScrollIndicator={false}>
<View style={{...}}>
<Text style={{...}}>
Add Transaction Details
</Text>
<ForwardRef(Themed.Input) placeholder="Enter name" leftIcon={{...}} autoFocus={true} value="ashwith" autoCapitalize="characters" returnKeyType="next" onSubmitEditing={[Function: onSubmitEditing]} onChangeText={[Function: onChangeText]} inputStyle={{...}} containerStyle={{...}} inputContainerStyle={{...}} errorMessage={{...}} />
<DatePicker style={{...}} date="2019/06/24" mode="date" placeholder="Select transaction date" format="YYYY/MM/DD" minDate="2019-01-01" confirmBtnText="Confirm" cancelBtnText="Cancel" iconComponent={{...}} customStyles={{...}} onCloseModal={[Function: onCloseModal]} onDateChange={[Function: _callee]} androidMode="default" height={259} duration={300} iconSource={{...}} showIcon={true} disabled={false} allowFontScaling={true} hideText={false} TouchableComponent={[Function]} modalOnResponderTerminationRequest={[Function: modalOnResponderTerminationRequest]} />
<ForwardRef(Themed.Input) placeholder="Enter transaction id" leftIcon={{...}} autoCapitalize="characters" returnKeyType="next" onSubmitEditing={[Function: onSubmitEditing]} onChangeText={[Function: onChangeText]} inputStyle={{...}} containerStyle={{...}} inputContainerStyle={{...}} errorMessage={{...}} />
<ForwardRef(Themed.Input) placeholder="Enter transaction type" leftIcon={{...}} autoCapitalize="characters" returnKeyType="next" onSubmitEditing={[Function: onSubmitEditing]} onChangeText={[Function: onChangeText]} inputStyle={{...}} containerStyle={{...}} inputContainerStyle={{...}} errorMessage={{...}} />
<ForwardRef(Themed.Input) placeholder="Enter Amount" leftIcon={{...}} keyboardType="numeric" returnKeyType="done" onSubmitEditing={[Function: onSubmitEditing]} onChangeText={[Function: onChangeText]} inputStyle={{...}} containerStyle={{...}} inputContainerStyle={{...}} errorMessage={{...}} />
</View>
</ScrollViewMock>
</KeyboardAvoidingView>
<View style={{...}}>
<ForwardRef(Themed.Button) title="Submit" buttonStyle={{...}} onPress={[Function: onPress]} containerStyle={{...}} />
</View>
</MainContainer>

Related

Hai , I am using react formik for post and edit method in single components I am facing some difficulty here

Problem is when I am updating the form and page redirect to list page then when we click on the name of the edit form at that time it will shows the old data not what I entered the current data.
This is the problem I am facing
`# I tried with some of the react formik methods but it wont help me to updating dom properly so please someone help with this, give some idea thanks in advance.
#code
import React, { useEffect, useState, useCallback, useRef } from "react";
import moment from 'moment'
import * as Yup from "yup";
import PropTypes from "prop-types";
import { useSnackbar } from "notistack";
import { Form, FormikProvider, useFormik } from "formik";
// material
import { LoadingButton } from "#mui/lab";
import { TimePicker } from '#mui/x-date-pickers/TimePicker';
import {
Box,
Card,
Grid,
Stack,
TextField,
Typography,
FormHelperText,
ToggleButtonGroup,
ToggleButton,
Alert,
Autocomplete,
CircularProgress,
InputAdornment,
} from "#mui/material";
import { useNavigate, useParams } from "react-router-dom";
// utils
import { fData } from "../../utils/formatNumber";
import { UploadAvatar } from "../../../src/components/upload/index";
import countries from "../../data/countries";
import { useDispatch, useSelector } from "../../redux/store";
import {
postBranch,
postBranchPhoto,
updateIsBranchDone,
} from "../../redux/slices/dashboard";
import { setGlobalState, useGlobalState } from "src/contexts/GlobalState";
import { postDefaultBranch } from "src/redux/slices/branch";
import apiUrls from "../../config/api-urls";
import useIsMountedRef from "../../hooks/useIsMountedRef";
import useAuth from "../../hooks/useAuth";
import useMediaQuery from "#material-ui/core/useMediaQuery";
import PhoneInput from "src/components/PhoneInput";
import { ApiHelper } from "src/redux/api-helpers";
import { AdapterDayjs } from '#mui/x-date-pickers/AdapterDayjs';
import dayjs from 'dayjs';
import {
useFindBranchByIdQuery,
useFindBranchQuery,
} from "src/redux/apiServices/branchService";
import { AccountCircle } from "#mui/icons-material";
import { set } from "lodash";
var data = require("../../data/timezones.json");
function countryToFlag(isoCode) {
return typeof String.fromCodePoint !== "undefined"
? isoCode
.toUpperCase()
.replace(/./g, (char) =>
String.fromCodePoint(char.charCodeAt(0) + 127397)
)
: isoCode;
}
const apiKey = "AIzaSyBz0QZLgUyySO2mWAy5KcOHFXMpWkGYPOA";
const mapApiJs = "https://maps.googleapis.com/maps/api/js";
//load googleMap Api js
function loadAsyncScript(src) {
return new Promise((resolve) => {
const script = document.createElement("script");
Object.assign(script, {
type: "text/javascript",
async: true,
src,
});
script.addEventListener("load", () => resolve(script));
document.head.appendChild(script);
});
}
EditBranchProfile.propTypes = {
currentUser: PropTypes.object,
};
let handleTimeout;
export default function EditBranchProfile({ isAdd, branchDetails }) {
// data from store
const searchInput = useRef(null);
const dispatch = useDispatch();
const navigate = useNavigate();
const [defaultCompany] = useGlobalState("defaultCompany");
const {
user,
branchdefault,
branchName,
companyName,
company,
branch: authBranch,
} = useAuth();
const { err_msg, branch, branch_photo, isBranchDone } = useSelector(
(state) => state.dashboard
);
console.log(branch,"branches from edit")
// const params = useParams();
// let [defaultBranch] = useGlobalState("defaultBranch"); // defaultBranch
// // RTKQ
// const { isLoading, data: branchDetails = {} } = useFindBranchByIdQuery(params?.id || defaultBranch?.id, { skip: !params?.id && !defaultBranch?.id })
// console.log(branchDetails, 'branchDetails')
// const branchDetails = params.id ? branchData : defaultBranch
// State
const [placeAddress, setAddress] = useState({});
const [places, setPlaces] = useState(
branchDetails?.address ? [{ label: branchDetails.address, id: null }] : []
);
const [isPlacesLoading, setIsPlacesLoading] = useState(false);
const [holiday, setHoliday] = useState([]);
const [toggle, setToggle] = useState({ defaultbranch: false });
const [alignment, setAlignment] = useState("");
const [value, setValue] = React.useState(new Date());
// Hooks
const { enqueueSnackbar } = useSnackbar();
const isMountedRef = useIsMountedRef();
const matches = useMediaQuery("(min-width:600px)");
//Multiple buttons data
const holidays = [
{ id: 1, day: "Sunday" },
{ id: 2, day: "Monday" },
{ id: 3, day: "Tuesday" },
{ id: 4, day: "Wednesday" },
{ id: 5, day: "Thursday" },
{ id: 6, day: "Friday" },
{ id: 7, day: "Saturday" },
];
const teamSizeOptions = [
{ label: "1-10", code: 10 },
{ label: "11-50", code: 50 },
{ label: "51-150", code: 100 },
{ label: "150+", code: 150 },
];
const getTeamSizeOptions = (label) => {
let option = teamSizeOptions.find((item) => item.label === label);
if (!option) return teamSizeOptions;
return teamSizeOptions.filter((item) => {
return item.code <= option.code;
});
};
// const extractAddress = (place) => {
// const placeAddress = {
// city: "",
// state: "",
// country: "",
// zip: "",
// place_id: "",
// }
// if (!Array.isArray(place?.address_components)) {
// return placeAddress;
// }
// }
const phoneRegExp = /^[0-9]{6,13}$/;
const SUPPORTED_FORMATS = [
"image/jpg",
"image/jpeg",
"image/gif",
"image/png",
];
const NewBranchSchema = Yup.object().shape({
name: Yup.string().required("Name is required"),
// contact_mobile: Yup.string().required('Mobile number is required').matches(/^[0-9\s]+$/, 'Only numbers are allowed for this field '),
// contact_mobile: Yup.string()
// .required("Phone number is required")
// .min(6,"Phone number is not valid")
// .max(13,"Phone number is not valid")
// .matches(phoneRegExp, "Phone number is not valid"),
// c_code: Yup.string().required("Country code is required"),
address: Yup.mixed().required("address is required"),
time_zone: Yup.string().required("Time Zone is required"),
total_people: Yup.mixed().required("Team Size is required"),
pin: Yup.string().required("ZipCode is required"),
holiday: Yup.array().required("Holiday is required"),
country: Yup.string().required("country is required"),
state: Yup.string().required("State is required"),
city: Yup.string().required("City is required"),
opt_from: Yup.string().required("Operates From is required"),
opt_to: Yup.string().required("Operates To is required"),
// avatarUrl: Yup.mixed().required('Avatar is required')
// .test(
// "fileFormat",
// "Unsupported Format",
// value => value && SUPPORTED_FORMATS.includes(value.type)
// )
// .test(
// "fileSize",
// "File too large",
// value => value && value.size <= FILE_SIZE
// )
});
const formik = useFormik({
enableReinitialize: true,
initialValues: {
name: isAdd ? "" : branchDetails ? branchDetails.name : "",
contact_mobile: isAdd ? "": branchDetails ? branchDetails.contact_mobile: "",
c_code: isAdd ? "" : branchDetails ? branchDetails.c_code : "IN",
total_people: isAdd ? "": branchDetails ? branchDetails.total_people: "",
time_zone: isAdd ? "" : branchDetails ? branchDetails.time_zone : "",
address: isAdd ? "" : branchDetails?.address || null,
country: isAdd ? "" : branchDetails ? branchDetails.country : "",
state: isAdd ? "" : branchDetails ? branchDetails.state : "",
city: isAdd ? "" : branchDetails ? branchDetails.city : "",
pin: isAdd ? "" : branchDetails ? branchDetails.pin : "",
opt_from: moment(isAdd ? "00:00" : branchDetails ? branchDetails.opt_from : "00:00","HH:mm"),
opt_to: moment(isAdd ? "00:00" : branchDetails ? branchDetails.opt_to : "00:00","HH:mm"),
// avatarUrl: isAdd ? "":`${apiUrls.BRANCH_PHOTO}${branchDetails ? branchDetails.logo : branch ? branch.logo : ""}`,
// avatarUrl: `${apiUrls.BRANCH_PHOTO}${branch ? branch.logo : ''}`,
avatarUrl:isAdd ? " " : `${apiUrls.BRANCH_PHOTO}${branchDetails ? branchDetails.logo : ''}`,
logo: null,
holiday: isAdd ? [] : branchDetails ? branchDetails.holiday : "",
// defaultbranch: branch ? branch.defaultbranch : 'YES',
},
validationSchema: NewBranchSchema,
onSubmit: async (values, { setSubmitting, resetForm, setErrors }) => {
setSubmitting(true);
try {
let data = JSON.parse(JSON.stringify(values));
data["company_id"] = defaultCompany ? defaultCompany.id : company.id;
data["created_by"] = user ? user.id : "";
// data['c_code'] = data['c_code','phone'];
data["opt_from"] = moment(data.opt_from).format("HH:mm");
data["opt_to"] = moment(data.opt_to).format("HH:mm");
data["holiday"] = holiday;
if (!isAdd) {
values["logo"] = branch?.logo;
dispatch(postBranch(data,branch.id));
resetForm();
} else {
dispatch(postBranch(data));
resetForm({values:''})
}
// if (isBranchDone) {
// resetForm();
// }
} catch (error) {
console.log(error);
if (isMountedRef.current) {
setErrors({ afterSubmit: error.message || err_msg });
setSubmitting(false);
}
}
},
});
const {
errors,
values,
touched,
handleSubmit,
isSubmitting,
setFieldValue,
getFieldProps,
setFieldTouched,
setValues,
isValid,
dirty,
handleReset
} = formik;
useEffect(() => {
return () => {
dispatch(updateIsBranchDone(false))
}
}, [])
useEffect(() => {
if (err_msg) {
errors.afterSubmit = err_msg;
}
if (branch) {
setHoliday(branch.holiday);
if (!branch_photo && values["avatarUrl"].path) {
const formData = new FormData();
fetch(values["avatarUrl"].preview).then((response) => {
response.blob().then((blob) => {
formData.append("photo", blob, values["avatarUrl"].path);
dispatch(postBranchPhoto(formData, branch.id));
});
});
}
}
if (isBranchDone) {
enqueueSnackbar(err_msg || isAdd ? 'Branch created successfully' : 'Branch updated successfully', {
variant: err_msg ? 'error' : 'success'
})
setValues({values})
navigate('/dashboard/branches')
}
}, [err_msg, isBranchDone]);
const handleDrop = useCallback(
(acceptedFiles) => {
const file = acceptedFiles[0];
if (file) {
setFieldValue("avatarUrl", {
...file,
preview: URL.createObjectURL(file),
});
}
},
[setFieldValue]
);
const handleButton = (button) => {
if (holiday.includes(button)) {
setHoliday(holiday.filter((el) => el !== button));
} else {
setHoliday([...holiday, button]);
}
};
const handleAlignment = (event, newAlignment) => {
if (newAlignment !== null) {
setAlignment(newAlignment);
}
};
const toggler = (event) => {
setToggle({ ...toggle, [event.target.name]: event.target.checked });
};
const searchPlaces = ({ target }) => {
let { value } = target;
clearTimeout(handleTimeout);
handleTimeout = setTimeout(() => {
setIsPlacesLoading(true);
ApiHelper({
url: `${apiUrls.UTILS.ADDRESS}?search=${value}`,
method: "get",
})
.then((res) => res.data)
.then((res) => {
if (!res.error) {
let places = res.data || [];
places = places.map((item) => ({
id: item.placeId,
label: item.fullAddress,
}));
setPlaces(places);
}
setIsPlacesLoading(false);
});
}, 200);
};
const handlePlaseChange = (e, value) => {
let { id, label } = value;
setFieldTouched("address", true);
if (!value.id) return;
ApiHelper({
url: `${apiUrls.UTILS.PLACE_DETAILS}?placeId=${id}`,
method: "get",
})
.then((res) => res.data)
.then((res) => {
if (!res.error) {
let address = res.data;
setValues({
...values,
address: label,
pin: address.pinCode || "",
country: address.country || "",
state: address.state || "",
city: address.city || "",
});
}
});
};
// if(!defaultBranch) {
// return <div>Loading...</div>
// }
return (
<FormikProvider
value={formik}
validateOnMount
onSubmit={handleSubmit}
enableReinitialize={true}
>
<Form noValidate autoComplete="off"
onReset={handleReset}
>
<Grid container spacing={3}>
<Grid item xs={12} md={4}>
<Card sx={{ py: 10, px: 3 }}>
<Box sx={{ mb: 5 }}>
<UploadAvatar
accept="image/*"
file={values.avatarUrl}
maxSize={3145728}
onDrop={handleDrop}
id="logo-photo"
error={Boolean(touched.avatarUrl && errors.avatarUrl)}
caption={
<Typography
variant="caption"
sx={{
mt: 2,
mx: "auto",
display: "block",
textAlign: "center",
color: "text.secondary",
}}
>
Allowed *.jpeg, *.jpg, *.png, *.gif
<br /> max size of {fData(3145728)}
</Typography>
}
/>
<FormHelperText error sx={{ px: 2, textAlign: "center" }}>
{touched.avatarUrl && errors.avatarUrl}
</FormHelperText>
</Box>
</Card>
</Grid>
<Grid item xs={12} md={8}>
<Card sx={{ p: 3 }}>
<Stack spacing={3}>
{errors.afterSubmit &&
<Alert severity="error">{errors.afterSubmit}</Alert>}
<TextField
fullWidth
label="Branch Name"
{...getFieldProps("name")}
error={Boolean(touched.name && errors.name)}
helperText={touched.name && errors.name}
/>
{/* <PhoneInput
label="Branch Mobile Number"
country={countries.find(
(country) => country.code === values.c_code
)}
onCountryChange={({ code }) => setFieldValue("c_code", code)}
text={values.contact_mobile}
onTextChange={({ target }) =>
setFieldValue("contact_mobile", target.value)
}
error={Boolean(
touched.contact_mobile && errors.contact_mobile
)}
helperText={touched.contact_mobile && errors.contact_mobile}
/> */}
<Autocomplete
fullWidth
value={values.total_people}
//need to change it as string option----
options={getTeamSizeOptions(company.team_size).map(
(option) => option.label
)}
renderInput={(params) => (
<TextField
{...params}
error={Boolean(
touched.total_people && errors.total_people
)}
helperText={touched.total_people && errors.total_people}
label="Branch Team Size"
/>
)}
onChange={(e, value) => {
if (value) setFieldValue("total_people", value);
}}
sx={{ mb: 2 }}
/>
{/* Address */}
<Autocomplete
fullWidth
value={values.address}
options={places}
loading={isPlacesLoading}
onChange={handlePlaseChange}
renderInput={(params) => (
<TextField
{...params}
onChange={searchPlaces}
label="Address"
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{isPlacesLoading ? (
<CircularProgress color="inherit" size={20} />
) : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
// {...getFieldProps("address")}
error={touched.address && !!errors.address}
helperText={touched.address && errors.address}
/>
)}
/>
<Stack
direction={{ xs: "column", sm: "row" }}
spacing={{ xs: 3, sm: 2 }}
>
<TextField
fullWidth
label="Country"
value={placeAddress.country}
{...getFieldProps("country")}
error={Boolean(touched.country && errors.country)}
helperText={touched.country && errors.country}
/>
<TextField
fullWidth
label="State/Region"
value={placeAddress.state}
{...getFieldProps("state")}
error={Boolean(touched.state && errors.state)}
helperText={touched.state && errors.state}
/>
</Stack>
<Stack
direction={{ xs: "column", sm: "row" }}
spacing={{ xs: 3, sm: 2 }}
>
<TextField
fullWidth
label="City"
{...getFieldProps("city")}
error={Boolean(touched.city && errors.city)}
helperText={touched.city && errors.city}
/>
<TextField
fullWidth
label="Zip/Pin Code"
{...getFieldProps("pin")}
error={Boolean(touched.pin && errors.pin)}
helperText={touched.pin && errors.pin}
/>
</Stack>
<Stack
direction={{ xs: "column", sm: "row" }}
spacing={{ xs: 3, sm: 2 }}
>
<Autocomplete
fullWidth
freeSolo
value={values.time_zone}
options={data.map((option) => option.text)}
renderInput={(params) => (
<TextField
{...params}
error={Boolean(touched.time_zone && errors.time_zone)}
helperText={touched.time_zone && errors.time_zone}
label="time_zone"
{...getFieldProps("state")}
/>
)}
onChange={(e, value) => {
if (value) setFieldValue("time_zone", value);
}}
sx={{ mb: 2 }}
/>
{/* <TextField
id="input-with-icon-adornment"
fullWidth
type="time"
// defaultValue={'05:10'}
label="Store opening time"
placeholder="Store start from"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<AccountCircle />
</InputAdornment>
),
}}
{...getFieldProps("opt_from")}
InputLabelProps={{ shrink: true }}
error={Boolean(touched.opt_from && errors.opt_from)}
helperText={touched.opt_from && errors.opt_from}
/> */}
{/* <TimePicker
label="Store opening time"
value={values.opt_from}
onChange={(e, value) => {
if (value) setValue(value);
}}
renderInput={(params) => <TextField {...params} />}
{...getFieldProps("opt_from")}
error={Boolean(touched.opt_from && errors.opt_from)}
helperText={touched.opt_from && errors.opt_from}
/> */}
<TimePicker
label="Store opening time"
renderInput={(params) => <TextField
{...params}
fullWidth
onBlur={() => setFieldTouched('opt_from', true)}
error={Boolean(touched.opt_from && errors.opt_from)}
helperText={touched.opt_from && errors.opt_from}
/>}
value={values.opt_from}
onChange={value => setFieldValue('opt_from', value)}
/>
{/* <TextField
fullWidth
type="time"
label="Operates To"
// placeholder="Operates To"
{...getFieldProps('opt_to')}
error={Boolean(touched.opt_to && errors.opt_to)}
helperText={touched.opt_to && errors.opt_to}
/> */}
{/* <TextField
fullWidth
type="time"
label="Store closing time"
placeholder="Operates To"
{...getFieldProps("opt_to")}
InputLabelProps={{ shrink: true }}
error={Boolean(touched.opt_to && errors.opt_to)}
helperText={touched.opt_to && errors.opt_to}
/> */}
<TimePicker
label="Store closing time"
renderInput={(params) => <TextField
{...params}
fullWidth
onBlur={() => setFieldTouched('opt_to', true)}
error={Boolean(touched.opt_to && errors.opt_to)}
helperText={touched.opt_to && errors.opt_to}
/>}
value={values.opt_to}
onChange={value => setFieldValue('opt_to', value)}
InputLabelProps={{ shrink: true }}
/>
</Stack>
{/* <Stack direction={{ xs: 'column', sm: 'row' }}
spacing={{ xs: 3, sm: 2 }}>
<Typography component="p" sx={{ mt: '8px' }}>
Default Branch
</Typography>
<Switch
onChange={toggler}
name="defaultbranch"
// checked={checkedValue}
// {...getFieldProps('defaultbranch')}
// error={Boolean(touched.defaultbranch && errors.defaultbranch)}
// helperText={touched.defaultbranch && errors.defaultbranch}
/>
</Stack> */}
<Stack
direction={{ xs: "column", sm: "row" }}
spacing={{ xs: 3, sm: 2 }}
>
<Typography component="p" sx={{ mt: "8px" }}>
Weekly Off
</Typography>
<ToggleButtonGroup
// color="secondary"
value={holiday}
onChange={handleAlignment}
orientation={`${matches ? `horizontal` : `vertical`}`}
// exclusive
fullWidth
// onChange={handleChange}
// {...getFieldProps('holiday')}
// error={Boolean(touched.holiday && errors.holiday)}
// helperText={touched.holiday && errors.holiday}
>
{holidays.map((holiday) => (
<ToggleButton
key={holiday.id}
value={holiday.day}
onClick={() => handleButton(holiday.day)}
>
{holiday.day}
</ToggleButton>
))}
</ToggleButtonGroup>
</Stack>
<Box
sx={{
mt: 3,
display: "flex",
justifyContent: "flex-start",
}}
>
<LoadingButton
type="submit"
variant="contained"
size="large"
loading={isSubmitting}
// disabled={!(isValid && dirty)}
>
{isAdd ? "Create Branch" : "Save Changes"}
</LoadingButton>
</Box>
</Stack>
</Card>
</Grid>
</Grid>
</Form>
</FormikProvider>
);
}
`

Failed prop type: Invalid prop `defaultValue` of type `object`

Hy! I'm new in react native but I'm facing this issue.
I design the OTP screen using TextInput but having that error I don't know why, failed prop type invalid prop 'defaultValue' of type 'object' supplied to 'textinput' expected 'string'.
I used state hook and useref hook to do create this screen.
here's complete code of screen:
//import files
const EmailOTP = ({ navigation }) => {
const [otpState, setOtpState] = useState({
pin1: '',
pin2: '',
pin3: '',
pin4: '',
pin5: '',
pin6: '',
})
otpState.pin1 = useRef('');
otpState.pin2 = useRef('');
otpState.pin3 = useRef('');
otpState.pin4 = useRef('');
otpState.pin5 = useRef('');
otpState.pin6 = useRef('');
useEffect(() => {
otpState.pin1.current.focus();
}, [])
return (
<View style={styles.container} >
<ScrollView
contentInsetAdjustmentBehavior="automatic"
showsVerticalScrollIndicator={false}
>
<View style={styles.OTPWrapper} >
<TextInput
ref={otpState.pin1}
onChangeText={pin1 => {
setOtpState({ pin1: pin1 })
if (otpState.pin1 != "") {
otpState.pin2.current.focus();
}
}
}
defaultValue={otpState.pin1}
maxLength={1}
style={styles.OTPinput}
/>
<TextInput
ref={otpState.pin2}
onChangeText={pin2 => {
setOtpState({ pin2: pin2 })
if (otpState.pin2 != "") {
otpState.pin3.current.focus();
}
}
}
defaultValue={otpState.pin2}
maxLength={1}
style={styles.OTPinput}
/>
<TextInput
ref={otpState.pin3}
onChangeText={pin3 => {
setOtpState({ pin3: pin3 })
if (otpState.pin3 != "") {
otpState.pin4.current.focus();
}
}
}
defaultValue={otpState.pin3}
maxLength={1}
style={styles.OTPinput}
/>
<TextInput
ref={otpState.pin4}
onChangeText={pin4 => {
setOtpState({ pin4: pin4 })
if (otpState.pin4 != "") {
otpState.pin5.current.focus();
}
}
}
defaultValue={otpState.pin4}
maxLength={1}
style={styles.OTPinput}
/>
<TextInput
ref={otpState.pin5}
onChangeText={pin5 => {
setOtpState({ pin5: pin5 })
if (otpState.pin5 != "") {
otpState.pin6.current.focus();
}
}
}
defaultValue={otpState.pin5}
maxLength={1}
style={styles.OTPinput}
/>
<TextInput
ref={otpState.pin6}
onChangeText={pin6 => {
setOtpState({ pin6: pin6 })
}
}
defaultValue={otpState.pin6}
maxLength={1}
style={styles.OTPinput}
/>
</View>
<TouchableOpacity onPress={()=>navigation.navigate('PhoneNumberVerification')}>
<View style={[styles.btnWrapper, { marginTop: 40, }]} >
<Text style={styles.logButton} >Verify Email</Text>
</View>
</TouchableOpacity>
<View style={styles.contentWrapperOtpResend} >
<View style={styles.loginRedirectWrapper}>
<Text style={styles.loginRedirectText} >Didn't Receive the code?</Text>
<Text style={styles.loginRedirectButton}> Resend</Text>
</View>
</View>
</ScrollView>
</View>
);
}
export default EmailOTP
The way I see it, you actually have just linked the pins but have not set them. Try this instead:
otpState.pin1 = useRef(null);
otpState.pin2 = useRef(null);
otpState.pin3 = useRef(null);
otpState.pin4 = useRef(null);
otpState.pin5 = useRef(null);
otpState.pin6 = useRef(null);
otpState.pin1.current = '';
otpState.pin2.current = '';
otpState.pin3.current = '';
otpState.pin4.current = '';
otpState.pin5.current = '';
otpState.pin6.current = '';
And set the defaultValue of every TextInput to their respective pins like this : 'otpState.pin1.current'
This is how the TextInput should look like:
<TextInput
ref={otpState.pin1}
onChangeText={pin1 => {
setOtpState({pin1: pin1});
if (otpState.pin1 != '') {
otpState.pin2.current.focus();
}
}}
defaultValue={otpState.pin1.current}
maxLength={1}
style={styles.OTPinput}
/>

Passing data from a functional component to a parent class component react native

So I have a tab navigation in my app and I am trying to scan a UPC number from on of the tab screens which is called ScanScreen and send it to the homeScreen where I have a search bar and I want the upc number to show up there once the navigation has been done. So far I am able to scan the upc number and navigate to the homeScreen but for some reason I am not able to send the upc number. I have tried many ways ex. navigation.navigate('HomeScreen',{upc: data}) and the getParam() on the home screen but nothing works.
Obj = scan the upc and then successfully navigate to the homeScreen tab and show the number on the searchBar
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = { search: '', isLoading: true };
this.products = [];
}
componentDidMount() {
db.collection("products")
.get()
.then(querySnapshot => {
const data = querySnapshot.docs.map(doc => doc.data());
this.setState({
products: data,
isLoading: false,
dataSource: data
},
function () {
this.products = data;
}
);
}).catch(error => {
console.error(error);
});
}
GetFlag(ingredients, status) {
var Ingredients = Object.values(ingredients);
for (var i = 0; i < Ingredients.length; i++) {
if (Ingredients[i].Status == status)
return true;
}
}
search = text => {
console.log(text);
};
clear = () => {
this.search.clear();
};
SearchFilterFunction(text) {
const newData = this.products.filter(function (item) {
const itemData = item.Name ? item.Name.toUpperCase() :
''.toUpperCase();
const upcData = item.UPC;
const textData = text.toUpperCase();
if (itemData.indexOf(textData) > -1)
return true;
else if (upcData.toString().indexOf(textData) > -1)
return true;
else
return false;
});
this.setState({
dataSource: newData, search: text,
});
}
ListViewItemSeparator = () => {
return (
<View
style={{
height: 0.4,
width: '100%',
backgroundColor: '#141313',
}}
/>
);
};
render() {
const { navigate } = this.props.navigation;
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 21 }}>
<ActivityIndicator />
</View>
);
}
const upc = navigate.getParams('upcCode')
console.log(upc)
return (
<View style={styles.viewStyle}>
<SearchBar round
searchIcon={{ size: 25 }}
onChangeText={text => this.SearchFilterFunction(text)} onClear={text => this.SearchFilterFunction('')}
placeholder="Type Here to Search..." value={this.state.search}
/>
{
this.state.search.trim() != "" ?
<FlatList data={this.state.dataSource}
ItemSeparatorComponent={this.ListViewItemSeparator}
renderItem={({ item }) => (
<View style={styles.container}>
<TouchableHighlight onPress={() => this.props.navigation.navigate('Summary', {
ProductObject: item
})}
>
<View >
{
this.GetFlag(item.ingredients, 'Haram') == true ?
<Image source={images['Haram']} style={styles.imageView} /> :
this.GetFlag(item.ingredients, 'Halal') == true ?
<Image source={images['Halal']} style={styles.imageView} /> :
this.GetFlag(item.ingredients,'Doubtful') == true ?
<Image source={images['Doubtful']} style={styles.imageView} /> :
null
}
<Text style={styles.baseText}>
<Text style={styles.titleText} >
{item.Name}{'\n'}{'\n'}
</Text>
</Text>
</View>
</TouchableHighlight>
</View>
)}
enableEmptySections={true}
style={{ marginTop: 11 }}
keyExtractor={(item, index) => index.toString()}
/> : <FlatList></FlatList>
}
</View>
);
}
}
export default function ScanScreen({navigation}) {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
navigation.navigate("Home",{
upcCode: data
})
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-end',
}}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
{scanned && <Button title={'Tap to Scan Again'} onPress={() => setScanned(false)} />}
</View>
);
}
Below is my AppNavigator.JS code if it helps seeing the navigation
function MainTabNavigator() {
return (
<Tab.Navigator tabBarOptions={{
activeTintColor: 'red',
inactiveTintColor: 'gray'
}}
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
let iconName;
let tabBarVisible = true;
if (route.name == 'Home') {
iconName = 'md-home'
} else if (route.name == 'Scan') {
iconName = 'md-camera'
} else if (route.name == 'Menu') {
iconName = "md-menu"
}
else if (route.name == 'Login') {
iconName = "md-log-in"
}
return <Ionicons name={iconName} color={color} size={size} />
}
})}
>
<Tab.Screen
name='Home'
component={HomeScreen}
options={({route}) => ({
title: "Search for Products",
headerBackTitleVisible: false,
headerStyle: {
backgroundColor: '#1c2845',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize:22,
fontFamily: 'DamascusLight'
}
})}
/>
<Tab.Screen
name='Login'
component={LoginScreen}
/>
<Tab.Screen name='Scan' component={ScanScreen} />
<Tab.Screen name='Menu' component={SearchScreen} />
</Tab.Navigator>
)
}
function MainStackNavigator() {
return (
<NavigationContainer>
<Stack.Navigator>
{/* <Stack.Screen name='Login' component={LoginScreen} /> */}
<Stack.Screen name='HomeScreen' component={MainTabNavigator}
options={({ route }) => ({
title: 'Search for Products',
headerBackTitleVisible: false,
headerStyle: {
backgroundColor: '#1c2845',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 22,
fontFamily: "DamascusLight"
},
}
)} />
<Stack.Screen name='RegistrationScreen' component={RegistrationScreen} />
<Stack.Screen
name="Summary"
component={SummaryScreen}
options={({ route }) => ({
headerTitle: () =>
<View style={styles.header}>
<Text numberOfLines={1} style={styles.title}>{route.params.title}</Text>
<Text numberOfLines={1} style={styles.subtitle}>{route.params.subtitle}</Text>
</View>,
headerBackTitleVisible: false,
headerStyle: {
backgroundColor: '#1c2845',
height: 100
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 12,
fontFamily: "DamascusLight"
},
}
)}
/>
<Stack.Screen
name="ScanSummary"
component={ScanSummary}
// options={({route}) => ({
// headerTitle: () =>
// <View style={styles.header}>
// <Text numberOfLines={1}>{route.params.title}</Text>
// <Text numberOfLines={1}>{route.params.subtitle}</Text>
// </View>,
// headerStyle: {
// backgroundColor: '#1c2845',
// height: 100
// },
// headerTintColor: '#fff',
// headerTitleStyle: {
// fontSize: 12,
// fontFamily: "DamascusLight"
// },
// })}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default MainStackNavigator
if you are using react navigation 5
you can access by params by
this.props.route.params
if older
this.props.navigation.state.params

Formik - Plug custom validation for custom component into my currently working Formik form

I've got a form with some text inputs, and some custom components. I've got the Formik validation working on the text inputs but not the custom components. I am now trying to add Formik validation to my custom categoriesMultiselect component. This component holds its data in the redux store. I have handled the validation myself and added a value to my redux props:
const mapStateToProps = (
state: RecordOf<VepoState>,
ownProps: { rerenderKey: boolean }
) => ({...
isCategoriesValid: selectIsCategoriesValid(state),
...
})
. I just want to plug props.isCategoriesValid into my Formik form.
From reading the official documentation, I think I do that by adding validate={validateCategories} as a prop to the custom component and adding the function validateCategories to the component. So I have tried that like this:
//above React render()
validateCategories = () => {
let error
if (!this.props.selectIsCategoriesValid) {
error = 'please select a category'
}
return error
}
// Inside React render()
<CategoriesMultiselect.View
validate={this.validateCategories}
name={'categories'}
label={'Product Categories'}
categoryCodes={[CategoryEnums.CategoryCodes.Grocery]}
/>
validateCategories never gets run. That's why I have tested running it by adding validateField to one of my inputs onChange function:
<Input
label={'Product Brand'}
value={values.brand}
onTouch={setFieldTouched}
error={touched.brand && errors.brand}
placeholder="Enter Brand"
name="brand"
required
onChange={() => validateField('categories')}
deleteText={setFieldValue}
/>
When it tries to validate the field, I get console error:
Cannot read property 'validate' of undefined
on this line of code in Formik:
var validateField = useEventCallback(function (name) {
if (isFunction(fieldRegistry.current[name].validate)) {
I have at least plugged Formik into my Redux because I am at least successfully dispatching a Redux action when submitting the form. What am I doing wrong?
Code:
//#flow
import * as Yup from 'yup'
import { Formik, withFormik } from 'formik'
import { Container } from 'native-base'
import * as React from 'react'
import { ScrollView, View, Alert, Button } from 'react-native'
import { connect } from 'react-redux'
import { Category as CategoryEnums } from 'src/enums'
import type { VepoState } from 'src/components/model'
import type { RecordOf } from 'immutable'
import type { Product } from 'src/model'
import VepoHeader from 'src/components/formControls/header/view'
import { selectIsAddFormValid } from './selector'
import { selectProduct } from './selector'
// import { Button } from 'src/components/formControls'
import { ImagePicker } from 'src/components/formControls'
import LocationAutocomplete from 'src/components/formControls/locationAutocomplete/view'
import { uploadAddProduct, updateRerenderKey } from './action'
import { viewStyle } from './style'
import type { Dispatch } from 'redux'
import { updateAddProductImage } from './action'
import type { Place } from 'src/model/location'
import { Colors, Spacing } from 'src/styles'
import { Input } from 'src/components/formControls'
import { onPress } from './controller'
import { CategoriesMultiselect } from 'src/components/formControls'
import {
selectIsGrocerySelected,
selectIsCategoriesValid,
isLocationValid
} from 'src/components/product/add/groceryItem/selector'
const mapStateToProps = (
state: RecordOf<VepoState>,
ownProps: { rerenderKey: boolean }
) => ({
locationListDisplayed: state.formControls.root.locationListDisplayed,
isAddFormValid: selectIsAddFormValid(state),
// $FlowFixMe
product: selectProduct(state),
// $FlowFixMe
isGrocerySelected: selectIsGrocerySelected(state),
// $FlowFixMe
categories: state.formControls.categories,
isCategoriesValid: selectIsCategoriesValid(state),
image: state.product.add.image,
rerenderKey: ownProps.rerenderKey,
location: state.formControls.location,
isLocationValid: isLocationValid(state)
})
// eslint-disable-next-line flowtype/no-weak-types
const mapDispatchToProps = (dispatch: Dispatch<*>): Object => ({
updateAddProductImage: (value): void => {
dispatch(updateAddProductImage({ value }))
},
uploadAddProduct: (product: Product): void => {
dispatch(uploadAddProduct(product))
},
updateRerenderKey: () => {
dispatch(updateRerenderKey())
}
})
export const getLocationIsValid = (place: Place): boolean => {
return Object.keys(place).length > 0 ? true : false
}
type AddGroceryStoreState = {
name: string,
brand: string,
description: string,
price?: number
}
class AddGroceryItemView extends React.Component<any, AddGroceryStoreState> {
validateCategories = () => {
let error
if (!this.props.selectIsCategoriesValid) {
error = 'please select a category'
}
return error
}
render() {
const {
values,
handleSubmit,
setFieldValue,
errors,
touched,
setFieldTouched,
isValid,
isSubmitting,
validateField
} = this.props
return (
<Container>
<VepoHeader title={'Add Vegan Grocery Product'} />
<Container style={container}>
<ScrollView
keyboardShouldPersistTaps="always"
style={viewStyle(this.props.locationListDisplayed).scrollView}>
<View>
<LocationAutocomplete
label={'Grocery Store'}
placeHolder={'Enter Grocery Store'}
/>
</View>
<View style={viewStyle().detailsContainer}>
<ImagePicker
label={'Product Image (optional)'}
image={this.props.image.image}
updateAddProductImage={this.props.updateAddProductImage}
updateRerenderKey={this.props.updateRerenderKey}
/>
<Input
label={'Product Name'}
onTouch={setFieldTouched}
value={values.name}
placeholder="Enter Name"
name="name"
required
error={touched.name && errors.name}
deleteText={setFieldValue}
onChange={setFieldValue}
/>
<Input
label={'Product Brand'}
value={values.brand}
onTouch={setFieldTouched}
error={touched.brand && errors.brand}
placeholder="Enter Brand"
name="brand"
required
onChange={() => validateField('categories')}
deleteText={setFieldValue}
/>
<View>
<Input
label={'Product Description'}
value={values.description}
placeholder="Enter Description"
multiline={true}
required
onTouch={setFieldTouched}
error={touched.description && errors.description}
numberOfLines={4}
name="description"
deleteText={setFieldValue}
onChange={setFieldValue}
/>
<Input
isValid={true}
isPrice={true}
label={'Product Price'}
value={values.price}
onTouch={setFieldTouched}
error={touched.price && errors.price}
placeholder="Enter Price"
name="price"
deleteText={setFieldValue}
onChange={setFieldValue}
/>
<View>
<CategoriesMultiselect.View
validate={this.validateCategories}
name={'categories'}
label={'Product Categories'}
categoryCodes={[CategoryEnums.CategoryCodes.Grocery]}
/>
</View>
</View>
</View>
</ScrollView>
</Container>
<Button
title="submit"
onPress={handleSubmit}
disabled={!isValid || isSubmitting}
loading={isSubmitting}
/>
{/* <Button.View onSub={this._handleSubmit} onPress={this._handleSubmit} label={'GO!'} /> */}
</Container>
)
}
}
const container = {
flex: 1,
...Spacing.horizontalPaddingLarge,
backgroundColor: Colors.greyLight,
flexDirection: 'column'
}
const formikEnhancer = withFormik({
validationSchema: Yup.object().shape({
name: Yup.string().required(),
brand: Yup.string().required(),
categories: Yup.array(),
description: Yup.string()
.min(9)
.required(),
price: Yup.number()
.typeError('price must be a number')
.required()
}),
mapPropsToValues: () => ({
name: '',
brand: '',
description: '',
price: '',
categories: []
}),
handleSubmit: (values, { props }) => {
props.updateRerenderKey()
},
displayName: 'AddGroceryItemView'
})(AddGroceryItemView)
// $FlowFixMe
const AddGroceryItemViewComponent = connect(
mapStateToProps,
mapDispatchToProps
)(formikEnhancer)
export default AddGroceryItemViewComponent
As Rikin requested, here is the CategoriesMultiselect component:
//#flow
import type { Node } from 'react'
import { selectSelectedCategory } from 'src/components/product/add/groceryItem/selector'
import type { VepoState } from 'src/components/model'
import type { RecordOf } from 'immutable'
import { connect } from 'react-redux'
import * as React from 'react'
import { View } from 'react-native'
import {
List,
ListItem,
Text,
Left,
Body,
Right,
Button,
Container,
Label,
Title,
Content
} from 'native-base'
import Icon from 'react-native-vector-icons/FontAwesome'
import Eicon from 'react-native-vector-icons/EvilIcons'
import Modal from 'react-native-modal'
import SelectMultiple from 'react-native-select-multiple'
import {
updateAlertModalIsOpen,
updateAlertModalHasYesNo,
updateAlertModalMessage,
updateAlertModalTitle
} from 'src/components/formControls/alertModal/action'
import * as C from './model'
import type { Subcategory } from 'src/model/category'
import * as controller from './controller'
import { getIsCategoriesValid } from './controller'
import { styles } from 'src/components/style'
import {
Colors,
Corners,
Distances,
Modals,
Spacing,
Typography,
ZIndexes
} from 'src/styles'
import { Containers } from '../../../styles'
import {
toggleSubcategory,
setAllShowSubcategoriesToFalse,
toggleShowSubcategories
} from './action'
import type { Dispatch } from 'redux'
const mapStateToProps = (state: RecordOf<VepoState>) => ({
vepo: state,
// $FlowFixMe
selectedCategory: selectSelectedCategory(state),
categories: state.formControls.categories
})
// eslint-disable-next-line flowtype/no-weak-types
const mapDispatchToProps = (dispatch: Dispatch<*>): Object => ({
setAllShowSubcategoriesToFalse: (): void => {
dispatch(setAllShowSubcategoriesToFalse())
},
toggleSubcategory: (sc): void => {
return dispatch(toggleSubcategory(sc))
},
toggleShowSubcategories: (c): void => {
dispatch(toggleShowSubcategories(c))
},
updateAlertModalIsOpen: (isOpen: boolean): void => {
dispatch(updateAlertModalIsOpen(isOpen))
},
updateAlertModalMessage: (message: string): void => {
dispatch(updateAlertModalMessage(message))
},
updateAlertModalHasYesNo: (hasYesNo: boolean): void => {
dispatch(updateAlertModalHasYesNo(hasYesNo))
},
updateAlertModalTitle: (title: string): void => {
dispatch(updateAlertModalTitle(title))
}
})
const renderCategoryRow = (props: C.CategoriesViewProps, item: C.Category) => {
return (
<View>
<ListItem
style={listItem}
icon
onPress={() => controller.categoryClicked(props, item)}>
<Left>
<Icon
style={styles.icon}
name={item.icon}
size={20}
color={item.iconColor}
/>
</Left>
<Body style={[styles.formElementHeight, border(item)]}>
<Text style={Typography.brownLabel}>{item.label}</Text>
</Body>
<Right style={[styles.formElementHeight, border(item)]}>
<Eicon style={catStyle.arrow} name="chevron-right" size={30} />
</Right>
</ListItem>
</View>
)
}
const getCategoriesToDisplay = (props) => {
const y = props.categories.filter((x) => props.categoryCodes.includes(x.code))
return y
}
class CategoriesMultiselectView extends React.Component {
setFormCategories = () => {
if (this.props && this.props.setFieldValue) {
this.props.setFieldValue(
'categories',
controller.getSelectedSubcategories(this.props.categories)
)
}
}
render(): React.Node {
const categoriesToDisplay = getCategoriesToDisplay(this.props)
return (
<View>
<View style={{ ...Containers.fullWidthRow }}>
<Label disabled={false} style={Typography.formLabel}>
{this.props.label}
</Label>
<View style={{ ...Containers.fullWidthRow }} />
<Label disabled={false} style={Typography.formLabel}>
{controller.getNumberOfSelectedSubcategories(this.props.categories)}{' '}
Selected
</Label>
</View>
<View
style={catStyle.categoriesViewStyle(this.props, categoriesToDisplay)}>
{this.props.categories && this.props.categories.length > 0 && (
<List
listBorderColor={'white'}
style={categoriesListStyle}
dataArray={categoriesToDisplay}
renderRow={(item: C.Category) => {
return renderCategoryRow(this.props, item)
}}
/>
)}
<View style={catStyle.modalConatinerStyle} />
<Modal
style={catStyle.modal}
onModalHide={this.setFormCategories}
isVisible={
this.props.categories
? this.props.categories.some((cat: C.Category) =>
controller.showModal(cat)
)
: false
}>
<Container style={catStyle.modalView}>
<View style={Modals.modalHeader}>
<Title style={catStyle.categoriesTitleStyle}>
{controller.getDisplayedCategoryLabel(this.props.categories)}
</Title>
<Right>
<Button
transparent
icon
onPress={this.props.setAllShowSubcategoriesToFalse}>
<Eicon name="close-o" size={25} color="#FFFFFF" />
</Button>
</Right>
</View>
<Content style={catStyle.categoryStyle.modalContent}>
<SelectMultiple
checkboxSource={require('../../../images/unchecked.png')}
selectedCheckboxSource={require('../../../images/checked.png')}
labelStyle={[
styles.label,
styles.formElementHeight,
styles.modalListItem
]}
items={controller.getDisplayedSubcategories(
this.props.categories
)}
selectedItems={controller.getSelectedSubcategories(
this.props.categories
)}
onSelectionsChange={(selections, item: Subcategory) => {
this.props.toggleSubcategory({ subcategory: item }).the
}}
/>
</Content>
</Container>
</Modal>
</View>
{this.props.error && (
<Label
disabled={false}
style={[
Typography.formLabel,
{ color: 'red' },
{ marginBottom: Spacing.medium }
]}>
{this.props.error}
</Label>
)}
</View>
)
}
}
const catStyle = {
// eslint-disable-next-line no-undef
getBorderBottomWidth: (item: C.Category): number => {
if (item.icon === 'shopping-basket') {
return Spacing.none
}
return Spacing.none
},
// eslint-disable-next-line no-undef
categoriesViewStyle: (props: C.CategoriesViewProps, categoriesToDisplay) => {
return {
backgroundColor: Colors.borderLeftColor(
getIsCategoriesValid(props.categories)
),
...Corners.rounded,
paddingLeft: Spacing.medium,
height: Distances.FormElementHeights.Medium * categoriesToDisplay.length,
overflow: 'hidden',
borderBottomWidth: Spacing.none
}
},
arrow: {
color: Colors.brownDark,
borderBottomColor: Colors.brownDark
},
icon: { height: Distances.FormElementHeights.Medium },
// eslint-disable-next-line no-undef
categoriesTitleStyle: {
...styles.title,
...Typography.titleLeftAlign
},
categoryStyle: {
modalContent: {
...Corners.rounded
}
},
modal: {
flex: 0.7,
height: 20,
marginTop: Spacing.auto,
marginBottom: Spacing.auto
},
modalView: {
backgroundColor: Colors.white,
height: 500,
...Corners.rounded
},
modalConatinerStyle: {
marginBottom: Spacing.medium,
color: Colors.brownDark,
backgroundColor: Colors.brownLight,
position: 'absolute',
zIndex: ZIndexes.Layers.Negative,
right: Spacing.none,
height: Distances.Distances.Full,
width: Distances.Distances.Full,
...Corners.rounded
}
}
const categoriesListStyle = {
flex: Distances.FlexDistances.Full,
color: Colors.brownDark,
backgroundColor: Colors.brownLight,
height: Distances.FormElementHeights.Double,
...Corners.notRounded,
marginRight: Spacing.medium
}
const border = (item: C.Category) => {
return {
borderBottomWidth: catStyle.getBorderBottomWidth(item),
borderBottomColor: Colors.brownMedium
}
}
const listItem = {
height: Distances.FormElementHeights.Medium
}
// $FlowFixMe
const CategoriesMultiselect = connect(
mapStateToProps,
mapDispatchToProps
)(CategoriesMultiselectView)
export default CategoriesMultiselect
Example of form level validation usage by directly setting property in the form categories with error message.
...
...
...
const validateCategories = (values, props) => {
let error = {}
if (!props.selectIsCategoriesValid) {
error.categories = 'please select a category'
}
return error
}
class AddGroceryItemView extends React.Component<any, AddGroceryStoreState> {
render() {
const { ... } = this.props
return (
<Container>
<VepoHeader title={'Add Vegan Grocery Product'} />
<Container style={container}>
<ScrollView
keyboardShouldPersistTaps="always"
style={viewStyle(this.props.locationListDisplayed).scrollView}>
<View>
...
</View>
<View style={viewStyle().detailsContainer}>
...
<View>
...
<View>
<CategoriesMultiselect.View
// validate={this.validateCategories}
name={'categories'}
label={'Product Categories'}
categoryCodes={[CategoryEnums.CategoryCodes.Grocery]}
/>
</View>
</View>
</View>
</ScrollView>
</Container>
...
</Container>
)
}
}
...
const formikEnhancer = withFormik({
validationSchema: Yup.object().shape({
...
}),
mapPropsToValues: () => ({
...
}),
handleSubmit: (values, { props }) => {
...
},
displayName: 'AddGroceryItemView',
validate: validateCategories
})(AddGroceryItemView)
// $FlowFixMe
const AddGroceryItemViewComponent = connect(
mapStateToProps,
mapDispatchToProps
)(formikEnhancer)
export default AddGroceryItemViewComponent
Updated Field level validation using Formik's Field:
However personally I would go with Form level validation as first line of defense you are relying on validationSchema which should take care of field level validation at first and then second line of defense you should go with form-level where you can place custom messaging after validationSchema passes the test. If you put in field level then you may end up in possibly rabbit hole where it may lead to difficulty in maintaining components and customize it for individual scenarios in your app. To me form level validation is explicit enough at one convenient place for all additional field level validation. Alternatively you can also put in all your validationSchema and form-level validation function in a single file and then import it in your main component where you are going to wrap withFormik HOC.
Either way its upto you on how you want based on your requirements.
Here's the official docs link: https://jaredpalmer.com/formik/docs/guides/validation#field-level-validation
And as per that:
Note: The / components' validate function will only
be executed on mounted fields. That is to say, if any of your fields
unmount during the flow of your form (e.g. Material-UI's
unmounts the previous your user was on), those fields will not
be validated during form validation/submission.
//#flow
...
import SelectMultiple from 'react-native-select-multiple'
...
import {
toggleSubcategory,
setAllShowSubcategoriesToFalse,
toggleShowSubcategories
} from './action'
...
import { Field } from 'formik'
...
class CategoriesMultiselectView extends React.Component {
setFormCategories = () => {
if (this.props && this.props.setFieldValue) {
this.props.setFieldValue(
'categories',
controller.getSelectedSubcategories(this.props.categories)
)
}
}
render(): React.Node {
const categoriesToDisplay = getCategoriesToDisplay(this.props)
return (
<View>
<View style={{ ...Containers.fullWidthRow }}>
...
</View>
<View
style={catStyle.categoriesViewStyle(this.props, categoriesToDisplay)}>
{...}
<View style={catStyle.modalConatinerStyle} />
<Modal
style={catStyle.modal}
onModalHide={this.setFormCategories}
isVisible={
this.props.categories
? this.props.categories.some((cat: C.Category) =>
controller.showModal(cat)
)
: false
}>
<Container style={catStyle.modalView}>
<View style={Modals.modalHeader}>
...
</View>
<Content style={catStyle.categoryStyle.modalContent}>
<Field name="categories" validate={validate_Function_HERE_which_can_be_via_props_or_locally_defined} render={({field, form}) =>
<SelectMultiple
checkboxSource={require('../../../images/unchecked.png')}
selectedCheckboxSource={require('../../../images/checked.png')}
labelStyle={[
styles.label,
styles.formElementHeight,
styles.modalListItem
]}
items={controller.getDisplayedSubcategories(
this.props.categories
)}
selectedItems={controller.getSelectedSubcategories(
this.props.categories
)}
onSelectionsChange={(selections, item: Subcategory) => {
this.props.toggleSubcategory({ subcategory: item }).the
}}
/>}
/>
</Content>
</Container>
</Modal>
</View>
{this.props.error && (
<Label
disabled={false}
style={[
Typography.formLabel,
{ color: 'red' },
{ marginBottom: Spacing.medium }
]}>
{this.props.error}
</Label>
)}
</View>
)
}
}
...
// $FlowFixMe
const CategoriesMultiselect = connect(
mapStateToProps,
mapDispatchToProps
)(CategoriesMultiselectView)
export default CategoriesMultiselect

How to get data from Api in TextInput use on React Native?

I have some issues with TextInput when getting the data from API when calling the function I'm declaring it to fetch the API and get the data Depending on the input
I have a warning like
can't find variable searchInput
and I'm defining the search input in the state and put them to the value of the input, I think the code is right or what?
Code
import React, { Component } from "react";
import {
StyleSheet,
Text,
View,
TextInput,
LinearGradient,
ActivityIndicator,
TouchableOpacity
} from "react-native";
import Icon from "react-native-vector-icons/Ionicons";
export default class Search extends Component {
constructor() {
super();
this.ApiKey = "****";
this.state = {
searchInput: "",
searchResult: null,
error: "",
isLoading: false
};
}
searchCity = async () => {
this.setState({
isLoading: true
});
const url = `http://api.openweathermap.org/data/2.5/weather?q=${searchInput}&units=metric&appid=${
this.ApiKey
}`;
await fetch(url)
.then(res => res.json())
.then(responseJson =>
this.setState({
isLoading: false,
searchResult: {
name: responseJson.name,
country: responseJson.sys.country,
main: responseJson.weather[0].main,
description: responseJson.weather[0].description,
temp: responseJson.main.temp
}
})
)
.catch(error => this.setState({ error }));
};
render() {
const { searchInput, searchResult, isLoading, error } = this.state;
if (!isLoading) {
return (
<View
style={{
backgroundColor: "#2b3654",
flex: 1,
justifyContent: "center",
alignItems: "center"
}}
>
{isLoading && <Text style={styles.Text}> Loading...</Text>}
<ActivityIndicator size="large" color="00ff00" />
</View>
);
} else if (error) {
return (
<View>
<Text>{error}</Text>
</View>
);
}
if (searchResult) {
return (
<LinearGradient colors={["rgba(0,0,0,0.05)", "rgba(0,0,0,0)"]}>
<View>
<Text>{searchResult.name}</Text>
<Text> {searchResult.main}</Text>
<Text> {searchResult.description}</Text>
<Text> {searchResult.temp}</Text>
</View>
</LinearGradient>
);
} else {
return (
<View style={styles.container}>
<View style={styles.searchSection}>
<TouchableOpacity onPress={this.searchCity}>
<Icon
style={styles.searchIcon}
name="ios-search"
size={15}
color="#fff"
/>
</TouchableOpacity>
<TextInput
style={styles.input}
placeholder="Find Your City.."
placeholderTextColor="#fff"
underlineColorAndroid="transparent"
autoCapitalize="none"
onChangeText={searchInput => {
this.setState({ searchInput });
}}
onSubmitEditing={this.searchCity}
value={searchInput}
/>
</View>
</View>
);
}
}
}
You need to use this.state.searchInput
The line should be:
const url = `http://api.openweathermap.org/data/2.5/weather?q=${this.state.searchInput}...`;
The problem is in your URL, you have used searchInput there...
use this.state.searchInput
or you can use destructuring
const { searchInput } = this.state;
const url = `http://api.openweathermap.org/data/2.5/weather?q=${searchInput}&units=metric&appid=${
this.ApiKey
}`;

Categories

Resources