I'm trying to make the invalid feedback validation disappear after being on the screen for 5 seconds. in my state i have an empty errors object, when the form is submitted, the api call catches any errors from the backend, and they are placed in the errors object, the inputs use conditionals based on the errors object to show the validation. I've tried creating a setTimeout function that sets the state to an empty object after 5 seconds, but this causes breaking glitches if the form is submitted again incorrectly. Any insights how I can do this?
Register.js
import React, { Component } from "react";
import axios from 'axios';
import classnames from 'classnames';
class Register extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
password: "",
password2: "",
errors: {}
};
this.onChange = this.onChange.bind(this);
}
onChange(e) {
// THIS FUNCTION MUST BE BOUND -SEE ABOVE BIND
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit = e => {
// ARROW FUNCTIONS DO NOT NEED TO BE BOUND
e.preventDefault();
const newUser = {
name: this.state.name,
email: this.state.email,
password: this.state.password,
password2: this.state.password2
};
this.setState({
email: "",
name: "",
password: "",
password2: ""
});
axios
.post("/api/users/register", newUser)
.then(res => console.log(res.data))
.catch(err => this.setState({ errors: err.response.data }));
};
render() {
const { errors } = this.state;
return (
<div>
<div className="register">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h1 className="display-4 text-center text-dark">Sign Up</h1>
<p className="lead text-center">Create your DevMuse account</p>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<input
type="text"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.name
})}
placeholder="Name"
name="name"
value={this.state.name}
onChange={this.onChange}
/>
{errors.name && (
<div className="invalid-feedback">{errors.name}</div>
)}
</div>
<div className="form-group">
<input
type="email"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.email
})}
placeholder="Email Address"
name="email"
value={this.state.email}
onChange={this.onChange}
/>
{errors.email ? (
<div className="invalid-feedback">{errors.email}</div>
) : (
<small className="form-text text-muted text-center">
This site uses Gravatar so if you want a profile image,
use a Gravatar email
</small>
)}
</div>
<div className="form-group">
<input
type="password"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.password
})}
placeholder="Password"
name="password"
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && (
<div className="invalid-feedback">{errors.password}</div>
)}
</div>
<div className="form-group">
<input
type="password"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.password2
})}
placeholder="Confirm Password"
name="password2"
value={this.state.password2}
onChange={this.onChange}
/>
{errors.password2 && (
<div className="invalid-feedback">{errors.password2}</div>
)}
</div>
<input
type="submit"
className="btn btn-info btn-block mt-4"
/>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Register;
Just clear any existing timeout before you initiate a new timeout:
componentWillUnmount() {
clearTimeout(this.clearError);
}
...
.catch((err) => {
this.setState({ errors: err.response.data });
clearTimeout(this.clearError); // clear previous timeout, if exists
this.clearError = setTimeout(() => {
this.setState({ errors: {} });
}, 5000);
});
Related
I am learning react and I have decided to create a React Front End that connects to Express.js on back-end. At the moment, I am trying to add validation on my register/login forms. I dont really understand how the validation system is implemented. I've noticed that tutorials use functional components for Formik/Yup however, I am using a class based component.
My register page doesn't display anything when i try to render it. Of course, I must have implemented this incorrectly, I cant figure out what to do. Thank you for help.
import React, { Component } from "react";
import { register } from "./UserFunctions";
import { Formik, Field } from "formik";
import * as Yup from "yup";
// const validationSchema = yup.object().shape({
// first_name: yup.string
// })
class Register extends Component {
constructor() {
super();
this.state = {
first_name: "",
last_name: "",
email: "",
password: "",
errors: {},
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
//new user object
const newUser = {
first_name: this.state.first_name,
last_name: this.state.last_name,
email: this.state.email,
password: this.state.password,
};
//if register function succesful, redirect to login page
register(newUser).then((res) => {
this.props.history.push(`/login`);
});
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<Formik
initialValues={{
first_name: "",
last_name: "",
email: "",
password: "",
}}
validationSchema={Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number."),
})}
>
{(props) => {
const {
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
} = props;
<form noValidate onSubmit={this.onSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={this.state.first_name}
onChange={this.onChange}
/>
{errors.first_name && touched.first_name && (
<div className="input-feedback">{errors.first_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={this.state.last_name}
onChange={this.onChange}
/>
{errors.last_name && touched.last_name && (
<div className="input-feedback">{errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={this.state.email}
onChange={this.onChange}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && touched.password && (
<div className="input-feedback">{errors.password}</div>
)}
</div>
<button
type="submit"
className="btn btn-lg btn-primary btn-block"
>
Register!
</button>
</form>;
}}
</Formik>
</div>
</div>
</div>
);
}
}
export default Register;
Formik validates the form when trying to submit, if there are any errors it doesn't allow the form to be submitted, it is upto you to display the error messages to the user when he tries to submit with errors see this page for reference
That is your code with functional component and with fewer code lines, it's working as expected
import React from "react";
import { useFormik } from "formik";
import { register } from "./UserFunctions";
import * as Yup from "yup";
const Register = ({ history }) => {
const validationSchema = Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number."),
});
const formik = useFormik({
initialValues: {
first_name: "",
last_name: "",
email: "",
password: "",
},
validationSchema: validationSchema,
onSubmit: (values) => {
const newUser = {
first_name: values.first_name,
last_name: values.last_name,
email: values.email,
password: values.password,
};
//if register function succesful, redirect to login page
register(newUser).then((res) => {
history.push(`/login`);
});
},
});
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<form noValidate onSubmit={formik.handleSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={formik.values.first_name}
onChange={formik.handleChange}
/>
{formik.errors.first_name && formik.touched.first_name && (
<div className="input-feedback">{formik.errors.first_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={formik.values.last_name}
onChange={formik.handleChange}
/>
{formik.errors.last_name && formik.touched.last_name && (
<div className="input-feedback">{formik.errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={formik.values.email}
onChange={formik.handleChange}
/>
{formik.errors.email && formik.touched.email && (
<div className="input-feedback">{formik.errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={formik.values.password}
onChange={formik.handleChange}
/>
{formik.errors.password && formik.touched.password && (
<div className="input-feedback">{formik.errors.password}</div>
)}
</div>
<button type="submit" className="btn btn-lg btn-primary btn-block">
Register!
</button>
</form>
;
</div>
</div>
</div>
);
};
export default Register;
First, you need not use state while using Form Libraries. The main purpose of using Form libraries is to encapsulate the state and reduce the boilerplate.
Formik, itself, is a container for your form state.
Please Note :- you should pass the values of the formik form data into the input fields by using values ( which contains your form data) and handleChange provided by formik
Formik takes onSubmit as a prop where you can get your form state after validation and then, here you can register for your new user.
Also, you can check the codesandbox link here.
import React, { Component } from "react";
// import { register } from "./UserFunctions";
import { Formik } from "formik";
import * as Yup from "yup";
const schema = Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number.")
});
class Register extends Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<Formik
initialValues={{
first_name: "",
last_name: "",
email: "",
password: ""
}}
validationSchema={schema}
// tell the formik to validate onBlur
validateOnBlur
onSubmit={(values) => {
// here you have the access to the form data
// values.first_name, values_last_name, values_email, values_password
//if register function succesful, redirect to login page
// register(values).then((res) => {
// this.props.history.push(`/login`);
// })
}}
>
{(props) => {
const {
touched,
errors,
handleSubmit,
values,
handleChange,
handleBlur,
} = props;
return (
<form noValidate onSubmit={handleSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={values.first_name}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.first_name && touched.first_name && (
<div className="input-feedback">
{errors.first_name}
</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={values.last_name}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.last_name && touched.last_name && (
<div className="input-feedback">{errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.password && touched.password && (
<div className="input-feedback">{errors.password}</div>
)}
</div>
<button
type="submit"
className="btn btn-lg btn-primary btn-block"
>
Register!
</button>
</form>
);
}}
</Formik>
</div>
</div>
</div>
);
}
}
export default Register;
I have an issue with accessing my object that i have in my state.
My Register form currently looks like this:
class Register extends Component {
state = {
formData: {
name: '',
email: '',
password: '',
password2: '',
},
};
onChange = (e) => {
const { formData } = this.state
this.setState({...formData, [e.target.name]: e.target.value });
};
render() {
const { name, email, password, password2 } = this.state;
const onSubmit = (e) => {
e.preventDefault();
if (password !== password2) {
console.log('password do not match');
} else {
console.log(this.state);
}
};
return (
<section className='container'>
<h1 className='large text-primary'>Sign Up</h1>
<p className='lead'>
<i className='fas fa-user'></i> Create Your Account
</p>
<form
className='form'
action='create-profile.html'
onSubmit={(e) => onSubmit(e)}>
<div className='form-group'>
<input
type='text'
placeholder='Name'
name='name'
value={name}
onChange={this.onChange}
required
/>
</div>
<div className='form-group'>
<input
type='email'
placeholder='Email Address'
name='email'
value={email}
onChange={this.onChange}
/>
<small className='form-text'>
This site uses Gravatar so if you want a profile image, use a
Gravatar email
</small>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Password'
name='password'
minLength='6'
value={password}
onChange={this.onChange}
/>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Confirm Password'
name='password2'
value={password2}
onChange={this.onChange}
minLength='6'
/>
</div>
<input type='submit' className='btn btn-primary' value='Register' />
</form>
<p className='my-1'>
Already have an account? <a href='login.html'>Sign In</a>
</p>
</section>
);
}
}
Right now I did change my state to:
state = {
// formData: {
// name: '',
// email: '',
// password: '',
// password2: '',
// },
name: '',
email: '',
password: '',
password2: '',
};
and just get this working just as it is with no formData Object. But how can I access this formData?
With some various attempts a was able to:
put data only in one form field. After typing Name value and after that typing email value, my name was erased from form.
- I was unable to type in any form fields.
- I was able to change state but not in my formData object. I had empty object and my fields values in state.
- tried const {...formData} = this.state, also did not worked
Not sure what is the issue.
Thanks for any clues
Try the following changes
class Register extends Component {
state = {
formData: {
name: '',
email: '',
password: '',
password2: '',
},
};
onChange = (e) => {
// setting formData in the state properly
const { formData } = this.state;
let newFormData = {...formData};
newFormData[e.target.name] = e.target.value;
this.setState({
formData:newFormData
});
};
// onSubmit Handler outside render
const onSubmitHandler = (e) => {
// Destructing required fields from state.formData and using it
const {password,password2} = this.state.formData;
e.preventDefault();
if (password !== password2) {
console.log('password do not match');
} else {
console.log(this.state);
}
};
render() {
// Destructing required fields from state.formData and using it
const { name, email, password, password2 } = this.state.formData;
return (
<section className='container'>
<h1 className='large text-primary'>Sign Up</h1>
<p className='lead'>
<i className='fas fa-user'></i> Create Your Account
</p>
<form
className='form'
action='create-profile.html'
onSubmit={this.onSubmitHandler}>
<div className='form-group'>
<input
type='text'
placeholder='Name'
name='name'
value={name}
onChange={this.onChange}
required
/>
</div>
<div className='form-group'>
<input
type='email'
placeholder='Email Address'
name='email'
value={email}
onChange={this.onChange}
/>
<small className='form-text'>
This site uses Gravatar so if you want a profile image, use a
Gravatar email
</small>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Password'
name='password'
minLength='6'
value={password}
onChange={this.onChange}
/>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Confirm Password'
name='password2'
value={password2}
onChange={this.onChange}
minLength='6'
/>
</div>
<input type='submit' className='btn btn-primary' value='Register' />
</form>
<p className='my-1'>
Already have an account? <a href='login.html'>Sign In</a>
</p>
</section>
);
}
}
I'd like to know what's wrong in my code in terms of why it's not sending username, email and password to my database via an Axios POST request. My Laravel endpoint works fine because I verified via PostMan. I'm just struggling with the front end portion of this. What am I doing wrong and how can I fix this?
There error I'm getting on my browser says: Cannot POST /register
import React, {Component} from 'react';
import axios from "axios";
class Register extends Component {
constructor(props) {
super(props);
this.state = {
username: "",
email: "",
password: ""
};
this.userNameHandler = this.userNameHandler.bind(this);
this.emailHandler = this.emailHandler.bind(this);
this.passwordHandler = this.passwordHandler.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
userNameHandler(e) {
this.setState({
username: e.target.value,
});
console.log(this.state.username);
}
emailHandler(e) {
this.setState({
username: e.target.value,
});
console.log(this.state.email);
}
passwordHandler(e) {
this.setState({
username: e.target.value,
});
console.log(this.state.password);
}
handleSubmit(e) {
const user = {
name: this.state.name,
email: this.state.email,
password: this.state.password
}
this.setState({
username: e.target.value
});
axios.post('http://127.0.0.1:8000/api/auth/signup', user)
.then(response => {
console.log(response);
console.log(response.data);
});
}
render() {
return (
<div className="container">
<div id="login-row" className="row justify-content-center align-items-center">
<div id="login-column" className="col-md-6">
<div id="login-box" className="col-md-12">
<form id="login-form" className="form" method="post" onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="username" className="text-info">Username:</label><br/>
<input type="text" name="username" id="username" className="form-control" onChange={this.userNameHandler}/>
</div>
<div className="form-group">
<label htmlFor="username" className="text-info">Email:</label><br/>
<input type="text" name="username" id="username" className="form-control" onChange={this.emailHandler}/>
</div>
<div className="form-group">
<label htmlFor="password" className="text-info">Password:</label><br/>
<input type="text" name="password" id="password" className="form-control" onChange={this.passwordHandler}/>
</div>
<div className="form-group">
<input type="submit" name="submit" className="btn btn-info btn-md"
value="Submit"/>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}
export default Register;
You haven't called e.preventDefault(), so you get a regular form submission to the URL specified in the action attribute. You don't have one of those, but the default URL to submit to is the current URL.
The Ajax request gets cancelled as the browser navigates away from the current page to load a new one (which is the server saying it doesn't support POST requests to that URL).
I'm setting up a signup form that displays errors below the input fields if the user makes a mistake. The way I have it setup right now, the form will add a div with the error below when the user tries to submit their info. My issue is that when there's an error, it adds the div and messes up the layout of the form because it has to move everything to make space for each error. Is there a way to just have an empty div there if there isn't any errors so that it doesn't mess with the layout when there is one? So like, instead of having margin for spacing between fields, it's an empty div for the errors.
import React, { Component } from "react";
import axios from "axios";
import classnames from "classnames";
import "./Signup.css";
class Signup extends Component {
constructor() {
super();
this.state = {
username: "",
email: "",
password: "",
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
const newUser = {
username: this.state.username,
email: this.state.email,
password: this.state.password
};
axios
.post("api/users/register", newUser)
.then(res => console.log(res.data))
.catch(err => this.setState({ errors: err.response.data }));
}
render() {
const { errors } = this.state;
return (
<div className="signup-form">
<form noValidate onSubmit={this.onSubmit}>
<h2>Sign Up</h2>
<p>It's free and only takes a minute.</p>
<hr />
<div className="form-group">
<label>Username</label>
<input
type="text"
name="username"
className={classnames("form-control form-control-md", {
"is-invalid": errors.username
})}
value={this.state.username}
onChange={this.onChange}
/>
{errors.username && (
<div className="invalid-feedback">{errors.username}</div>
)}
</div>
<div className="form-group">
<label>Email</label>
<input
type="text"
name="email"
className={classnames("form-control form-control-md", {
"is-invalid": errors.email
})}
value={this.state.email}
onChange={this.onChange}
/>
{errors.email && (
<div className="invalid-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label>Password</label>
<input
type="text"
name="password"
className={classnames("form-control form-control-md", {
"is-invalid": errors.password
})}
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && (
<div className="invalid-feedback">{errors.password}</div>
)}
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block btn-lg">
Sign Up
</button>
</div>
<p className="small text-center">
By clicking the Sign Up button, you agree to our <br />
Terms & Conditions, and{" "}
Privacy Policy
</p>
<div className="text-center">
Already have an account? Login here
</div>
</form>
</div>
);
}
}
export default Signup;
Yes, you can use visibility:hidden property of css.
<div style={{ visibility: error.email? 'visible': 'hidden'}}></div>
since visibility always takes up space, in both cases it is visible as well as hidden. so it won't mess with the layout.
I have a function in ReactJS that checks if string.trim() === "". The function should return false if none of the trimmed strings are equal to "", but it always returns true.
I take those strings from state.
import React, {Component} from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import './Contact.scss'
export default class Contact extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
subject: "",
message: ""
}
}
setName = (e) => {
this.setState({name: e.target.value});
};
setEmail = (e) => {
this.setState({email: e.target.value});
};
setSubject = (e) => {
this.setState({subject: e.target.value});
};
setMessage = (e) => {
this.setState({message: e.target.value});
};
validate = () => {
const { name, email, subject, message } = this.state;
return name.trim() === "" ||
email.trim() === "" ||
subject.trim() === "" ||
message.trim() === "";
};
render() {
const { name, email, subject, message } = this.state;
return (
<div id="Contact" className="Wrapper-contact">
<div className="Contact-container">
<div className="Contact-title">
HIT ME UP
</div>
<div className="Contact-content">
<form className="Form" action="https://formspree.io/a#gmail.com" method="POST">
<div className="Form-group">
<label>NAME</label>
<input
className="Form-field"
name="Name"
placeholder="Your Name"
value={name}
onChange={this.setName}
/>
</div>
<div className="Form-group">
<label>EMAIL</label>
<input
className="Form-field"
name="_replyto"
placeholder="Your Email"
value={email}
onChange={this.setEmail}
/>
</div>
<div className="Form-group">
<label>SUBJECT</label>
<input
className="Form-field"
name="Subject"
placeholder="Subject"
value={subject}
onChange={this.setSubject}
/>
</div>
<div className="Form-group">
<label>MESSAGE</label>
<textarea
className="Form-textarea"
name="Message"
placeholder="Your Message..."
rows="7"
value={message}
onChange={this.setMessage}
/>
</div>
<input type="hidden" name="_next" value="/"/>
<button
className="Contact-button"
type="submit"
disabled={!!this.validate() ? "true" : "false"}
>
SEND
</button>
</form>
<div className="Connect">
<span style={{padding: '5%'}}>Also, don't forget to connect with me!</span>
<a className="Connect-in" href="link" target="_blank" rel="noopener noreferrer">
<FontAwesomeIcon icon={['fab', 'linkedin']} size="2x"/>
</a>
</div>
</div>
</div>
</div>
)
}
}
And the function still returns true always.
You are not invoking the function, instead you are using its reference. Try to invoke it instead. Also you are using strings in expression blocks like {"true"}. So, it does not work like that. Try like this:
disabled={!!this.validate() ? true : false}
But, as you figured out yourself you can use your function without a ternary condition:
disabled={!!this.validate()}
Since it returns the boolean itself, not the string.
Apparently replacing
disabled={!!this.validate() ? "true" : "false"} with
disabled={!!this.validate()} did solve the problem.