How to create two reusable invoking custom hook? - javascript

I created some logic that is reusable code anywhere. I have an input.jsx component for handling input elements, and use-form.js custom hook for managing all of the stuff and I have a basic-form component for creating form logic. Inside of basic-form component, I'm calling the custom hook many times. That is tedious and quite repetitive* so I wanna create two reusable invoking custom hooks in the input.jsx component
input.component.jsx
const Input = (props) => {
return (
<div className={`form-control ${props.error && 'invalid'}`}>
<label htmlFor={props.id}>{props.label}</label>
<input
type={props.type}
id={props.id}
value={props.value}
onChange={props.changeHandler}
onBlur={props.blurHandler}
/>
{props.error && <p className="error-text">{props.label} must be entered!</p>}
</div>
);
};
use-input.js
import { useReducer } from 'react';
const inputReducer = (state, action) => {
switch (action.type) {
case 'INPUT':
return { value: action.value, isTouched: state.isTouched };
case 'BLUR':
return { isTouched: true, value: state.value };
case 'RESET':
return { value: '', isTouched: false };
}
return state;
};
export const useInput = (validateValue) => {
const [state, dispatch] = useReducer(inputReducer, {
value: '',
isTouched: false,
});
const valueIsValid = validateValue(state.value);
const error = !valueIsValid && state.isTouched;
const valueChangeHandler = (e) => {
dispatch({ type: 'INPUT', value: e.target.value });
};
const inputBlurHandler = () => {
dispatch({ type: 'BLUR' });
};
const reset = () => {
dispatch({ type: 'RESET' });
};
return {
value: state.value,
isValid: valueIsValid,
error,
valueChangeHandler,
inputBlurHandler,
reset,
};
};
basic-form.component.jsx
import { useInput } from '../hooks/use-input';
import Input from './input.component';
const BasicForm = () => {
const {
value: name,
isValid: nameIsValid,
error: nameHasError,
valueChangeHandler: nameChangeHandler,
inputBlurHandler: nameBlurHandler,
reset: nameReset,
} = useInput((value) => value.trim() !== '');
const {
value: surname,
isValid: surnameIsValid,
error: surnameHasError,
valueChangeHandler: surnameChangeHandler,
inputBlurHandler: surnameBlurHandler,
reset: surnameReset,
} = useInput((value) => value.trim() !== '');
const {
value: email,
isValid: emailIsValid,
error: emailHasError,
valueChangeHandler: emailChangeHandler,
inputBlurHandler: emailBlurHandler,
reset: emailReset,
} = useInput((value) => /^\S+#\S+\.\S+$/.test(value));
let formIsValid = false;
if (nameIsValid && surnameIsValid && emailIsValid) {
formIsValid = true;
}
const formSubmitHandler = (e) => {
e.preventDefault();
nameReset();
surnameReset();
emailReset();
};
return (
<form onSubmit={formSubmitHandler}>
<div className="control-group">
<Input
id="name"
label="First Name"
type="text"
error={nameHasError}
value={name}
changeHandler={nameChangeHandler}
blurHandler={nameBlurHandler}
/>
<Input
id="surname"
label="Last Name"
type="text"
error={surnameHasError}
value={surname}
changeHandler={surnameChangeHandler}
blurHandler={surnameBlurHandler}
/>
<Input
id="email"
label="Email"
type="email"
error={emailHasError}
value={email}
changeHandler={emailChangeHandler}
blurHandler={emailBlurHandler}
/>
</div>
<div className="form-actions">
<button disabled={!formIsValid}>Submit</button>
</div>
</form>
);
};
export default BasicForm;
The problem with that is we must return two values from input.jsx to basic-form.jsx. The first value shows the property isValid or not. Second, a function for reseting every individual input element. Also, we must receive a function that validates the logic from the parent to the children components. How can we solve this problem? I think we should use useRef, useImperativeHandle and forwardRef but how?
Please vote up for more people to see.

Related

React render [object Object] when using useReducer instead of JSX

So I was learning my React course today on Udemy and came across an error where the react app renders [object Object] instead of the JSX (which is supposed to be an empty input box), also, since the input box has onChange method, The input value is also unable to change. Here is the image of the application and also the code given below:-
import React, { useState, useEffect, useReducer } from "react";
import Card from "../UI/Card/Card";
import classes from "./Login.module.css";
import Button from "../UI/Button/Button";
const emailReducer = (state, action) => {
if(action.type === 'USER_INPUT'){
return {value: action.value, isValid: action.value.includes('#')};
}
if(action.type === 'INPUT_BLUR'){
return {value: state.value, isValid: state.value.includes('#')};
}
return { value: '', isValid: false };
};
const Login = (props) => {
// const [enteredEmail, setEnteredEmail] = useState("");
// const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState("");
const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
const [emailState, dispatchEmail] = useReducer(emailReducer, {
value: '',
isValid: false
});
useEffect(() => {
console.log("EFFECT RUNNING");
return () => {
console.log("EFFECT CLEANUP");
};
}, []);
const emailChangeHandler = (event) => {
dispatchEmail({type: 'USER_INPUT', value: event.target.value});
setFormIsValid(
emailState.isValid && enteredPassword.trim().length > 6
);
};
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
setFormIsValid(
emailState.isValid && event.target.value.trim().length > 6
);
};
const validateEmailHandler = () => {
dispatchEmail({type: 'INPUT_BLUR'});
};
const validatePasswordHandler = () => {
setPasswordIsValid(enteredPassword.trim().length > 6);
};
const submitHandler = (event) => {
event.preventDefault();
props.onLogin(emailState.value, enteredPassword);
};
return (
<Card className={classes.login}>
<form onSubmit={submitHandler}>
<div
className={`${classes.control} ${
emailState.isValid === false ? classes.invalid : ""
}`}
>
<label htmlFor="email">E-Mail</label>
<input
id="email"
value={emailState}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
</div>
<div
className={`${classes.control} ${
passwordIsValid === false ? classes.invalid : ""
}`}
>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={enteredPassword}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
</div>
<div className={classes.actions}>
<Button type="submit" className={classes.btn} disabled={!formIsValid}>
Login
</Button>
</div>
</form>
</Card>
);
};
export default Login;
Your emailState is an object with the following shape:
{ value: string, isValid: boolean }
So in your input field you need to use the value attribute:
<input
id="email"
value={emailState.value}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
You are passing the entire emailState object to the input when you likely want the nested value property. You can pass emailState.value to the input's value prop, or destructure the state beforehand and pass value directly.
const [{ isValid, value }, dispatchEmail] = useReducer(emailReducer, {
value: '',
isValid: false
});
...
<input
id="email"
value={value}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>

How to change Antd form initialValues depends at url or id?

I got same component with Antd form for add/edit article. With pathes in router
<Route path="/add" component={ !currentUser ? Login : ArticleEditor } />
<Route path="/article/:id/edit" component={ !currentUser ? Login : ArticleEditor } />
When I click "edit" button I add initialValues to form, than if I click "Create new article" url changes to "/add", but form didn't update values. Values remains from edited article. How to update form values? Tried to set initialValues depends at path, or "id" but its not worked. How to update antd form values in that case?
const initialValues = this.props.location.pathname === '/add' ? {} : {
title: this.props?.title,
body: this.props?.body,
description: this.props?.description
};
Here you can see the component code - codesandbox link
The main issue with the code is form fields are not reset when url is changed, you can detect path change in shouldComponentUpdate and set isLoading to true and rest should work.
Updating initialValues will not work because, antd does shallow compare and once initialValues are set, you will not be able to change them.
There was an issue in the logic of componentDidUpdate which I corrected as well.
import React from "react";
import ErrorsList from "../ErrorsList/ErrorsList";
import userService from "../../services/userService";
import { connect } from "react-redux";
import { push } from "react-router-redux";
import { Form, Input, Button } from "antd";
import { store } from "../../store";
import actionCreators from "../../actionCreators";
const formItemLayout = {
labelCol: { span: 24 },
wrapperCol: { span: 24 }
};
const formSingleItemLayout = {
wrapperCol: { span: 24, offset: 0 }
};
const mapStateToProps = (state) => ({
...state.editor
});
const mapDispatchToProps = (dispatch) => ({
onLoad: (payload) => dispatch(actionCreators.doEditorLoaded(payload)),
onUnload: () => dispatch(actionCreators.doEditorUnloaded()),
onUpdateField: (key, value) =>
dispatch(actionCreators.doUpdateFieldEditor(key, value)),
onSubmit: (payload, slug) => {
dispatch(actionCreators.doArticleSubmitted(payload));
store.dispatch(push(`/`)); //article/${slug}
},
onRedirect: () => dispatch(actionCreators.doRedirect())
});
class ArticleEditor extends React.Component {
constructor(props) {
super(props);
this.id = this.props.match.params.id;
const updateFieldEvent = (key) => (e) =>
this.props.onUpdateField(key, e.target.value);
this.changeTitle = updateFieldEvent("title");
this.changeDescription = updateFieldEvent("description");
this.changeBody = updateFieldEvent("body");
this.changeTagInput = updateFieldEvent("tagInput");
this.isLoading = true;
this.submitForm = () => {
const article = {
title: this.props.title,
description: this.props.description,
body: this.props.body,
tagList: this.props.tagInput.split(",")
};
const slug = { slug: this.props.articleSlug };
const promise = this.props.articleSlug
? userService.articles.update(Object.assign(article, slug))
: userService.articles.create(article);
this.props.onSubmit(promise, this.props.articleSlug);
};
}
componentDidUpdate(prevProps, prevState) {
if (this.props.match.params.id !== prevProps.match.params.id) {
if (prevProps.match.params.id) {
this.props.onUnload();
}
this.id = this.props.match.params.id;
if (this.id) {
return this.props.onLoad(userService.articles.get(this.id));
}
this.props.onLoad(null);
}
this.isLoading = false;
}
componentDidMount() {
if (this.id) {
this.isLoading = true;
return this.props.onLoad(userService.articles.get(this.id));
}
this.isLoading = false;
this.props.onLoad(null);
}
componentWillUnmount() {
this.props.onUnload();
}
shouldComponentUpdate(newProps, newState) {
if (this.props.match.params.id !== newProps.match.params.id) {
this.isLoading = true;
}
return true;
}
render() {
const { errors } = this.props;
const initialValues = {
title: this.props?.title,
body: this.props?.body,
description: this.props?.description,
tags: this.props?.tagList
};
return this.isLoading ? (
"loading..."
) : (
<div className="editor-page">
<div className="container page">
<div className="">
<div className="">
<ErrorsList errors={errors}></ErrorsList>
<Form
{...formItemLayout}
initialValues={initialValues}
onFinish={this.submitForm}
>
<Form.Item
label="Title"
name="title"
placeholder="Article Title"
rules={[
{
required: true,
message: "Please input article title"
}
]}
>
<Input onChange={this.changeTitle} />
</Form.Item>
<Form.Item
label="Description"
name="description"
placeholder="Short description"
rules={[
{
required: true,
message: "Please input article description"
}
]}
>
<Input onChange={this.changeDescription} />
</Form.Item>
<Form.Item
name="body"
label="Article Text"
placeholder="article text"
>
<Input.TextArea onChange={this.changeBody} />
</Form.Item>
<Form.Item name="tags" label="Tags" placeholder="Enter tags">
<Input onChange={this.changeTagInput} />
</Form.Item>
<Form.Item {...formSingleItemLayout}>
<Button
className="editor-form__btn"
type="primary"
htmlType="submit"
disabled={this.props.inProgress}
>
Submit Article
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ArticleEditor);
take a look at this forked codesandbox.
You have to clean the fields before you re-use the 'ArticleEditor' component. Here you are using the same component for two different route, hence it's not changing.
You have to check if you are editing or adding a new entry to the Editor. Your editor component may look like this then,
const ArticleEditor = props => {
const [form] = Form.useForm();
useEffect(() => {
if (props.match.params.id) form.setFieldsValue({value : 'Some values'})
else form.resetFields()
}, [props?.match?.params]);
return (
<Form form={form} onFinish={yourFinishMethod}>
//...your form fields
</Form>
)
}

Why is onInput not a function? A question about useEffect()

I am pretty new to react and hooks, and I'm struggling with useEffect(). I've watched all the vids and read all the docs and still can't quite wrap my head around the error I'm getting. ("onInput is not a function" when my New Article route loads). onInput points to a callback function in my form-hook.js. Why isn't it registering?
In my input.js component:
import React, { useReducer, useEffect } from 'react';
import { validate } from '../../util/validators';
import './Input.css';
const inputReducer = (state, action) => {
switch (action.type) {
case 'CHANGE':
return {
...state,
value: action.val,
isValid: validate(action.val, action.validators)
};
case 'TOUCH': {
return {
...state,
isTouched: true
}
}
default:
return state;
}
};
const Input = props => {
const [inputState, dispatch] = useReducer(inputReducer, {
value: props.initialValue || '',
isTouched: false,
isValid: props.initialValid || false
});
const { id, onInput } = props;
const { value, isValid } = inputState;
useEffect(() => {
console.log(id);
onInput(id, value, isValid)
}, [id, value, isValid, onInput]);
const changeHandler = event => {
dispatch({
type: 'CHANGE',
val: event.target.value,
validators: props.validators
});
};
const touchHandler = () => {
dispatch({
type: 'TOUCH'
});
};
//if statement to handle if you are updating an article and touch the category.... but it's valid
const element =
props.element === 'input' ? (
<input
id={props.id}
type={props.type}
placeholder={props.placeholder}
onChange={changeHandler}
onBlur={touchHandler}
value={inputState.value}
/>
) : (
<textarea
id={props.id}
rows={props.rows || 3}
onChange={changeHandler}
onBlur={touchHandler}
value={inputState.value}
/>
);
return (
<div
className={`form-control ${!inputState.isValid && inputState.isTouched &&
'form-control--invalid'}`}
>
<label htmlFor={props.id}>{props.label}</label>
{element}
{!inputState.isValid && inputState.isTouched && <p>{props.errorText}</p>}
</div>
);
};
export default Input;
useEffect(() => {onInput points to the onInput prop in NewArticle.js component where users can enter a new article.
import Input from '../../shared/components/FormElements/Input';
import { useForm } from '../../shared/hooks/form-hook';
const NewArticle = () => {
const [formState, inputHandler] = useForm({
title: {
value: '',
isValid: false
}
}, false );
return (
<Input
id="title"
element="input"
type="text"
label="Title"
onInput={inputHandler}
/> );
};
export default NewArticle;
...and then in my form-hook.js inputHandler is a callback. So, onInput points to a callback function through a prop. It was working, registering onInput as a function and then, all of a sudden it was throwing an error. What gives?
import { useCallback, useReducer } from 'react';
const formReducer = (state, action) => {
switch (action.type) {
case 'INPUT_CHANGE':
let formIsValid = true;
for (const inputId in state.inputs) {
if (!state.inputs[inputId]) {
continue;
}
if (inputId === action.inputId) {
formIsValid = formIsValid && action.isValid;
} else {
formIsValid = formIsValid && state.inputs[inputId].isValid;
}
}
return {
...state,
inputs: {
...state.inputs,
[action.inputId]: { value: action.value, isValid: action.isValid }
},
isValid: formIsValid
};
case 'SET_DATA':
return {
inputs: action.inputs,
isValid: action.formIsValid
};
default:
return state;
}
};
export const useForm = (initialInputs, initialFormValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialFormValidity
});
const inputHandler = useCallback((id, value, isValid) => {
dispatch({
type: 'INPUT_CHANGE',
value: value,
isValid: isValid,
inputId: id
});
}, []);
const setFormData = useCallback((inputData, formValidity) => {
dispatch({
type: 'SET_DATA',
inputs: inputData,
formIsValid: formValidity
});
}, []);
return [formState, inputHandler, setFormData];
};
Thanks, ya'll.
I can give you some advice on how to restructure your code. This will ultimately solve your problem.
Maintain a single source of truth
The current state of your UI should be stored in a single location.
If the state is shared by multiple components, your best options are to use a reducer passed down by the Context API (redux), or pass down the container component's state as props to the Input component (your current strategy).
This means you should remove the Input component's inputReducer.
The onInput prop should update state in the container component, and then pass down a new inputValue to the Input component.
The DOM input element should call onInput directly instead of as a side effect.
Remove the useEffect call.
Separation of Concerns
Actions should be defined separately from the hook. Traditionally, actions are a function that returns an object which is passed to dispatch.
I am fairly certain that the useCallback calls here are hurting performance more than helping. For example inputHandler can be restructured like so:
const inputChange = (inputId, value, isValid) => ({
type: 'INPUT_CHANGE',
value,
isValid,
inputId
})
export const useForm = (initialInputs, initialFormValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialFormValidity,
})
const inputHandler = (id, value, isValid) => dispatch(
inputChange(id, value, isValid)
)
}
Learn how to use debugger or breakpoints in the browser. You would quickly be able to diagnose your issue if you put a breakpoint inside your useEffect call.

Switching input field form validation in react

Currently, in my form, the input fields get validated as soon as the user types in something. Here's the code for that-
index.js
import React from "react";
import ReactDOM from "react-dom";
import ShowError from "./ShowError";
import "./styles.css";
class App extends React.Component {
state = {
email: "",
name: "",
mobile: "",
errors: {
email: "",
name: "",
mobile: ""
},
nameError: false,
emailError: false,
mobileError: false,
formError: false
};
showMsg = () => {
if (!this.state.formError) {
alert("Error");
}
};
validateFunc = (name, value) => {
let error = this.state.errors;
let nameError = this.state.nameError;
let emailError = this.state.emailError;
let mobileError = this.state.mobileError;
switch (name) {
case "name":
nameError = !/^[a-zA-Z ]+$/.test(value);
error.name = nameError ? " is Invalid" : "";
break;
case "email":
emailError = !/^([\w.%+-]+)#([\w-]+\.)+([\w]{2,})$/i.test(value);
error.email = emailError ? " is Invalid" : "";
break;
case "mobile":
mobileError = !/^[0-9]{10}$/.test(value);
error.mobile = mobileError ? " is Invalid" : "";
break;
}
this.setState({
errors: error,
nameError: nameError,
emailError: emailError,
mobileError: mobileError
});
};
handleInput = event => {
const name = event.target.name;
const value = event.target.value;
this.setState(
{
[name]: value
},
this.validateFunc(name, value)
);
};
handleSubmit = event => {
event.preventDefault();
let formError =
this.state.nameError || this.state.emailError || this.state.mobileError;
this.setState({
formError: formError
});
};
render() {
return (
<div className="App">
<h1>Basic Form Validation</h1>
<form className="FormStyle">
<input
className="FieldStyle"
type="text"
name="name"
placeholder="Name"
onChange={event => this.handleInput(event)}
/>
<input
className="FieldStyle"
type="email"
name="email"
placeholder="Email"
onChange={event => this.handleInput(event)}
/>
<input
className="FieldStyle"
type="number"
name="mobile"
placeholder="Mobile"
onChange={event => this.handleInput(event)}
/>
<button
className="FieldStyle"
type="submit"
onClick={event => this.handleSubmit(event)}
>
SUBMIT
</button>
</form>
<ShowError error={this.state.errors} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
ShowError.js
import React from "react";
const ShowError = props => {
return Object.keys(props.error).map((field, index) => {
if (props.error[field].length > 0) {
return (
<p key={index}>
{field} {props.error[field]}
</p>
);
} else {
return "";
}
});
};
export default ShowError;
Expected Behaviour- What I want is, the fields should get validated as soon as the user focuses on next input field or when 'tab' key is pressed, I don't want to validate while the user is typing but when the user switches the field. How can I achieve the above behaviour? Thanks a lot!
P.S. - Would be better if I can achieve this without using libraries like redux-forms.
Use onBlur so the event is triggered when the user leaves the component
onBlur={event => this.handleInput(event)}

How to disable form submit button until all input fields are filled?! ReactJS ES2015

Hi i found an answer to this for a single field form... but what if we have a form with multiple field?
this is fine for disabling it if you have 1 field but it does not work when you want to disable it based on many fields:
getInitialState() {
return {email: ''}
},
handleChange(e) {
this.setState({email: e.target.value})
},
render() {
return <div>
<input name="email" value={this.state.email} onChange={this.handleChange}/>
<button type="button" disabled={!this.state.email}>Button</button>
</div>
}
})
Here is a basic setup for form validation:
getInitialState() {
return {
email: '',
text: '',
emailValid: false, // valid flags for each field
textValid: false,
submitDisabled: true // separate flag for submit
}
},
handleChangeEmail(e) { // separate handler for each field
let emailValid = e.target.value ? true : false; // basic email validation
let submitValid = this.state.textValid && emailvalid // validate total form
this.setState({
email: e.target.value
emailValid: emailValid,
submitDisabled: !submitValid
})
},
handleChangeText(e) { // separate handler for each field
let textValid = e.target.value ? true : false; // basic text validation
let submitValid = this.state.emailValid && textvalid // validate total form
this.setState({
text: '',
textValid: textValid,
submitDisabled: !submitValid
})
},
render() {
return <div>
<input name="email" value={this.state.email} onChange={this.handleChangeEmail}/>
<input name="text" value={this.state.text} onChange={this.handleChangeText}/>
<button type="button" disabled={this.state.submitDisabled}>Button</button>
</div>
}
})
In a more elaborate setup, you may want to put each input field in a separate component. And make the code more DRY (note the duplication in the change handlers).
There are also various solutions for react forms out there, like here.
I would take a little bit different way here...
Instead of setting submitDisabled in every single onChange handler I would hook into lifecycle method to listen to changes.
To be exact into componentWillUpdate(nextProps, nextState). This method is invoked before every change to component - either props change or state change. Here, you can validate your form data and set flag you need - all in one place.
Code example:
componentWillUpdate(nextProps, nextState) {
nextState.invalidData = !(nextState.email && nextState.password);
},
Full working fiddle https://jsfiddle.net/4emdsb28/
This is how I'd do it by only rendering the normal button element if and only if all input fields are filled where all the states for my input elements are true. Else, it will render a disabled button.
Below is an example incorporating the useState hook and creating a component SubmitButton with the if statement.
import React, { useState } from 'react';
export function App() {
const [firstname, setFirstname] = useState('');
const [lastname, setLastname] = useState('');
const [email, setEmail] = useState('');
function SubmitButton(){
if (firstname && lastname && email){
return <button type="button">Button</button>
} else {
return <button type="button" disabled>Button</button>
};
};
return (
<div>
<input value={email} onChange={ e => setEmail(e.target.value)}/>
<input value={firstname} onChange={ e => setFirstname(e.target.value)}/>
<input value={lastname} onChange={ e => setLastname(e.target.value)}/>
<SubmitButton/>
</div>
);
};
This might help. (credits - https://goshakkk.name/form-recipe-disable-submit-button-react/)
import React from "react";
import ReactDOM from "react-dom";
class SignUpForm extends React.Component {
constructor() {
super();
this.state = {
email: "",
password: ""
};
}
handleEmailChange = evt => {
this.setState({ email: evt.target.value });
};
handlePasswordChange = evt => {
this.setState({ password: evt.target.value });
};
handleSubmit = () => {
const { email, password } = this.state;
alert(`Signed up with email: ${email} password: ${password}`);
};
render() {
const { email, password } = this.state;
const isEnabled = email.length > 0 && password.length > 0;
return (
<form onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Enter email"
value={this.state.email}
onChange={this.handleEmailChange}
/>
<input
type="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handlePasswordChange}
/>
<button disabled={!isEnabled}>Sign up</button>
</form>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<SignUpForm />, rootElement);
export default function SignUpForm() {
const [firstName, onChangeFirstName] = useState("");
const [lastName, onChangeLastName] = useState("");
const [phoneNumber, onChangePhoneNumber] = useState("");
const areAllFieldsFilled = (firstName != "") && (lastName != "") && (phoneNumber != "")
return (
<Button
title="SUBMIT"
disabled={!areAllFieldsFilled}
onPress={() => {
signIn()
}
}
/>
)
}
Similar approach as Shafie Mukhre's!

Categories