Cannot read properties of undefined (reading 'target) - javascript

first post here. I tried searching around the forum but none of the answers seems to work in my case.
I'm pretty new to React, and have an issue with my onChange handler.
In my parent component I have the following:
// User registration - Onchange handler
const handleChange = (value, event) => {
// console.log(event.target.value);
setFormValue({
...formValue,
[event.target.name]: value,
});
};
And basically the parent component renders a multi step registration form, which renders the different components based on a state, here is the conditional for the username:
{step === 1 ? <Username handleChange={handleChange} formValue={formValue} /> : null}
In the child I have the following input component:
export const Username = ({formValue, handleChange}) => {
const username = formValue;
return (
<View>
<Input
placeholder="Username..."
onChangeText={handleChange}
value={formValue.username}
name={username} />
</View>
)
}
And the input component:
const Input = (props, name) => {
return (
<Animated.View style={[styles.inputSection, inputAnimated]}>
<TextInput style={styles.input} placeholder={props.placeholder} placeholderTextColor={theme.colors.lightP} fontSize={16} {...props}/>
</Animated.View>
)
}
I'm not sure why I get the error:
Cannot read properties of undefined (reading 'target)
Would appreciate any help.
Console.log of event.target returns undefined.

Here in the documentation you can read for the onChange event
You are using onChangeText
So onChangeText has just text value, but if you want to have the native event you have to use onChange.
Then access it in you method
const handleChange = ({ nativeEvent: { eventCount, target, text} }) => {
// console.log(event.target.value);
setFormValue({
...formValue,
[target]: text,
});
};
EDITED
Since asked again how to access the name of text input.
Here this link shows how to handle text input
Also you can do it like this:
For example in your method you are giving a name from parameters, you can pass that name to the handler parameter :
export const Username = ({formValue, handleChange}) => {
const username = formValue;
return (
<View>
<Input
placeholder="Username..."
onChangeText={(value)=>handleChange(value,username)} {/* here username is the name you are passing to the value */}
value={formValue.username}
name={username} />
</View>
)
}
// then in your handle method
const handleChange = (value,name) => {
setFormValue({
...formValue,
name: value,
});
};

This happens because onChangeText passes the input's value automatically to the function that's assigned to it, not passing the event itself
If you want to use the event => use onChange instead of onChangeText

Related

Formik: target is undefined when passing Material UI Date Picker as prop to React Component

I'm trying to create a reusable component. where I am passing the form fields as prop. when I click on datepicker field. I'm getting this error:
TypeError: target is undefined
How can I fix this?
Here's how my input component looks like:
import { useField } from 'formik';
import { DatePicker } from '#material-ui/pickers';
export const DatePickerField = ({ label, ...props }) => {
const [field, meta] = useField(props);
return (
<DatePicker label={label} fullWidth {...field} {...props} />
);
};
Here's how the reusable component looks like:
import { Form } from 'formik'
export const ReusableComp = ({ fields }) => (
<Form noValidate>
{fields}
</Form>
)
Here's where I am using this component:
export const App = () => (
<ReusableComp fields={
<div className='mb-3'>
<DateTimePickerField
label='Start DateTime'
name='start_date_time'
/>
</div>
} />
)
Result of console.log(fields)
In my case I was using the Material UI DesktopDatePicker (with Typescript), and defining the onChange prop like this solved it:
onChange={(value): void => {
formik.setFieldValue("dueDate", value);
}}
You have to replace "dueDate" with your formik value name.
After lots of research and thanks #Rosen Tsankov for pointing my attention to the onChange function.
I have seen questions about the same error on SO which are not answered. so this may help them and anyone in future facing this error.
As #Rosen Tsankov have said the material ui DatePicker component returns the date value as the first argument of the onChange function.
the field returned from useField have the following: name, value, onChange, onBlur.
the onChange function returned from useField expects the first argument to be an event which in this case is the date value. that's why we get the error:
TypeError: target is undefined
Because formik is trying to access target property and date has no property target. something like this date.target and this is undefined
so here's how I have fixed it. instead of spreading the field. I have added name and value from field to DatePicker. then I have used the setFieldValue from formik to manually update the input value like so.
import { DatePicker } from '#material-ui/pickers';
import { useField, useFormikContext } from 'formik';
export const DatePickerField = ({ label, ...props }) => {
const [field, meta] = useField(props);
const { setFieldValue } = useFormikContext();
return (
<DatePicker
fullWidth
{...props}
label={label}
name={field.name}
value={field.value}
helperText={meta.error}
error={meta.touched && Boolean(meta.error)}
onChange={(value) => setFieldValue(field.name, value)}
/>
);
};
DatePicker expects onChange((date) => ...) but you are passing formik handler wich expects onChange((event) => ....)

onChange function of Formik Field API doesn't work with child component

I'm trying to validate a material-ui-dropzone component (It's upload files !) inside Formik Field API as a child component, but it doesn't' work well. I got this error when i upload a file :
TypeError: can't access property "type", target is undefined
I already tried to override the onChange function for trying to correctly add the file into the form object :
field.onChange = (e) => {
form.values.appLogo = e;
form.touched.appLogo = true;
}
but it doesn't work well. The object form contain what i want, but my UI render the previous state and it's problematic because i need to know that everything is OK to enable the "Next form step" button.
Here is the problematic part of my code :
const Step1 = (props) => {
return (
<Field name="appLogo">
{({ field, form, meta }) => (
/**
* Overriding of onChange
*/
<div>
{
(field.onChange = (e) => {
form.values.appLogo = e;
form.touched.appLogo = true;
})
}
<DropzoneArea
filesLimit={1}
acceptedFiles={["image/png"]}
dropzoneClass="dropzoneArea"
dropzoneText=""
showAlerts
{...field}
/>
</div>
)}
</Field>
);
};
Can you help me please ?
Thank to nguyễn-trần-tâm
My problem is finally solved with the following code :
<Field name="appLogo">
{({ field, form, meta }) => (
<DropzoneArea
filesLimit={1}
acceptedFiles={["image/png"]}
dropzoneClass="dropzoneArea"
dropzoneText=""
{...field}
onChange={(e) => {
props.setFieldValue("appLogo", e);
}}
showAlerts
/>
)}
</Field>
You shouldn't override onChange function. Can you try using setFieldValue instead https://github.com/formium/formik/issues/1243

Data is not changing using onDataChange after using reusable component in react native

I created a reusable component Text input and I want to do a simple validation but i cant get the input value as it always showing null.
This is where I am getting my username and password
<LoginTextBox placeholderName='Email'
value={this.state.username}
onChangeText={(username) => this.setState({ username })}
></LoginTextBox>
<LoginTextBox placeholderName='Password'
value={this.state.password}
onChangeText={(password) => this.setState({ password })}
></LoginTextBox>
<TouchableOpacity
onPress={this.onLogin}>
This is where I created LoginTextBox
render() {
return (
<TextInput
placeholder={this.props.placeholderName}
style={styles.input}>{this.props.value}</TextInput>
);
}
Following is my validation
onLogin=()=> {
const { username, password } = this.state;
if (username.match("bhaskarj61#gmail.com")) {
this.props.navigation.navigate('NewsFeed');
}
else {
Alert.alert('enter valid email')
}
}
onChangeText -attribute never gets attached to your TextInput -component.
You could do something similar like this.
LoginTextBox -component render-method
render(){
return(
<TextInput
{...this.props}
placeholder={this.props.placeholderName}
style={styles.input}>{this.props.value}</TextInput>
)
}
and here {...this.props} on TextInput component passes your onChangeText listener to actual React Native -component.
In this case this would pass value, placeholderName and onChangeText attributes to actual component. You could this way also shorthand placeholderName to just use placeholder.

How to listen to onChange of the Field component in React-Final-Form?

Redux-form "Field" component provides onChange property. A callback that will be called whenever an onChange event is fired from the underlying input. This callback allows to get "newValue" and "previousValue" for the Field.
React-final-form "Field" component doesn't have this property.
So, how I can get the same functionality?
React-final-form handles this functionality with a tiny external package.
Basically it is an additional component to add inside the form that binds to the element using its name:
<Field name="foo" component="input" type="checkbox" />
<OnChange name="foo">
{(value, previous) => {
// do something
}}
</OnChange>
The current documentation can be found here:
https://github.com/final-form/react-final-form-listeners#onchange
The idea under change detection is to subscribe to value changes of Field and call your custom onChange handler when value actually changes. I prepared simplified example where you can see it in action. Details are in MyField.js file.
As the result you can use it just as with redux-form:
<MyField
component="input"
name="firstName"
onChange={(val, prevVal) => console.log(val, prevVal)}
/>
2022 JANUARY UPDATE
While the code above still works (check the sandbox version) there is a case when the solutions requires more tweeks around it.
Here is an updated sandbox with an implementation via the hooks. It's based on a useFieldValue hook and OnChange component as a consumer of this hook. But the hook itself can be used separately when you need previous value between re-renders. This solution doesn't rely on meta.active of the field.
// useFieldValue.js
import { useEffect, useRef } from "react";
import { useField } from "react-final-form";
const usePrevious = (val) => {
const ref = useRef(val);
useEffect(() => {
ref.current = val;
}, [val]);
return ref.current;
};
const useFieldValue = (name) => {
const {
input: { value }
} = useField(name, { subscription: { value: true } });
const prevValue = usePrevious(value);
return [value, prevValue];
};
export default useFieldValue;
// OnChange.js
import { useEffect } from "react";
import useFieldValue from "./useFieldValue";
export default ({ name, onChange }) => {
const [value, prevValue] = useFieldValue(name);
useEffect(() => {
if (value !== prevValue) {
onChange(value, prevValue);
}
}, [onChange, value, prevValue]);
return null;
};
Another nice option is this answer: https://stackoverflow.com/a/56495998/3647991
I haven't used redux-form, but I added a super simple wrapper around the Field component to listen to onChange like this:
const Input = props => {
const {
name,
validate,
onChange,
...rest
} = props;
return (
<Field name={name} validate={validate}>
{({input, meta}) => {
return (
<input
{...input}
{...rest}
onChange={(e) => {
input.onChange(e); //final-form's onChange
if (onChange) { //props.onChange
onChange(e);
}
}}
/>
)}}
</Field>
);
};
One could use the Field's parse attribute and provide a function that does what you need with the value:
<Field
parse={value => {
// Do what you want with `value`
return value;
}}
// ...
/>
You need to use the ExternalModificationDetector component to listen for changes on the field component like this:
<ExternalModificationDetector name="abc">
{externallyModified => (
<BooleanDecay value={externallyModified} delay={1000}>
{highlight => (
<Field
//field properties here
/>
)}
</BooleanDecay>
)}
</ExternalModificationDetector>
By wrapping a stateful ExternalModificationDetector component in a
Field component, we can listen for changes to a field's value, and by
knowing whether or not the field is active, deduce when a field's
value changes due to external influences.
Via - React-Final-Form Github Docs
Here is a sandbox example provided in the React-Final-Form Docs: https://codesandbox.io/s/3x989zl866

Redux Form: Input stays with 'touched: false'

Wanted to validate my inputs and change the CSS depending of the user interaction.
Starting with a required validation method I wrap all my inputs component with a <Field> and pass to validate an array of func. Just required for now.
But for all my fields the value stay the same touched: false and error: "Required". If I touch or add stuff in the input, those values stay the same.
Validation
export const required = value => (value ? undefined : 'Required')
NameInput
import React from 'react';
import { Field } from 'redux-form'
import InputItem from 'Components/InputsUtils/InputItem';
import { required } from 'Components/InputsUtils/Validation';
const NameInput = () => (
<Field
name={item.spec.inputName}
type={item.spec.type}
component={InputItem}
validate={[required]}
props={item}
/>
);
export default NameInput;
InputItem
import React from 'react';
const InputItem = ({ spec, meta: { touched, error } }) => {
const { type, placeholder } = spec;
return (
<input
className="input"
type={type}
placeholder={placeholder}
/>
);
};
export default InputItem;
There are 2 solutions to solve the "touched is always false" issue.
1) Ensure that input.onBlur is called in your component
For an input:
const { input } = this.props
<input {...input} />
For custom form elements without native onBlur:
const { input: { value, onChange, onBlur } } = this.props
const className = 'checkbox' + (value ? ' checked' : '')
<div
className={className}
onClick={() => {
onChange(!value)
onBlur()
}}
/>
2) Declare your form with touchOnChange
const ReduxFormContainer = reduxForm({
form: 'myForm',
touchOnChange: true,
})(MyForm)
The redux-form controls its own props within your <input /> element as long as you use the spread operator to pass those props into your input.
For example, where you are doing const InputItem = ({ spec, meta: { touched, error } }) => ...
Try destructing the input from the Component: const InputItem = ({ input, spec, meta: { touched, error } }) => ...
And where you have your <input ... />, try doing the following:
<input
{...input}
className="input"
type={type}
placeholder={placeholder}
/>
The redux-form captures any onBlur and onChange events and uses its own methods to change the touched state. You just need to pass those along as shown above.
These are what you need: https://redux-form.com/7.1.2/docs/api/field.md/#input-props
Another point to consider:
I passed {...props} to my custom component, however, touched still remained false.
That is because although props contained the input object, my component couldn't
deduce onBlur from it. When explicitly stating <CustomComponent {...this.props} onBlur={this.props.input.onBlur}, it worked as expected.

Categories

Resources