How to use multiple forms in same React? - javascript

I want to make a multi-stage react form. In this particular code, only Zipcode will be asked and will be submitted on submitting the form. But how to bring other forms asking for Email, important thing~ I want the form for email after I submit Zipcode, as both email and zipcode will be sent to backend together.
import React, { useEffect, useState } from 'react';
const Rider_Signup = ()=>{
const [zipcode,setzipcode]=useState();
const [email,set_email]=useState();
const onSubmitform = async e =>{
e.preventDefault();
try{
const body={zipcode,email};
const response = await fetch("https://taxibackendf.herokuapp.com/api/service/signup",{
method:"POST",headers:{"Content-Type":"application/json"},
body:JSON.stringify(body)
})
const datainjson = await response.json();
window.location =`/driver/login`;
}catch(err){
console.log('Error')
}
}
return (
<div className="admin_form_div">
<form action="/initial" id="admin_form" name="admin_form" onSubmit={onSubmitform}
<input type="text" name="Zipcode" className="input" value={zipcode}
onChange={e =>setzipcode(e.target.value)}
/>
<button type="submit" className="confirm_btn" >Confirm</button>
</form>
</div>
);
};
export default Rider_Signup;

const [step, setstep] = useState(1);
const [formdata, setFormData] = useState({zip:"", email:""}); // use to hold input from user
const renderForm = () =>{
switch(step){
case 1: return <div>Form one with email<button onClick = {() => setstep(step+1)}>Submit</button></div>
case 2: return <div>Form with zip code <button onClick = {() => setstep(step+1)}>Submit</button></div>
default: return <div>Default case</div>
}
}
return (
renderForm()
)
`

Related

Why the function is invoked for every input in React component?

I try to learn react component rendering but the problem is that I have a login page with 2 input field and 1 button as:
function LoginPage() {
const [username, changeUsername] = useState('');
const [password, changePassword] = useState('');
const loginRequest = async (username, password) => {
let response = await service.loginRequest(username, password);
console.log(response);
}
return (
<Card hoverable className='transaction-button-card'>
<h1>Enter username and password</h1>
<input type="text"
placeholder="Username"
onChange={e => changeUsername(e.target.value)}
value={username}></input>
<input type="text"
placeholder="Password"
onChange={e => changePassword(e.target.value)}
value={password}></input>
<Button onClick={loginRequest(username, password)}
className='withdraw-deposit-button'>Login/Deposit</Button>
</Card>
);
}
export default LoginPage;
When the page is rendered the function loginRequest(username, password) automatically triggered once and for every input characters to input fields are also triggering the same function and sending request for each input char. How can I solve this problem? (I don't want to send request automatically when the page is opened and send request with only with button). I would appreciate if you define the problem.
`
function LoginPage() {
const [username, changeUsername] = useState('');
const [password, changePassword] = useState('');
const loginRequest = async (username, password) => {
let response = await service.loginRequest(username, password);
console.log(response);
}
return (
<Card hoverable className='transaction-button-card'>
<h1>Enter username and password</h1>
<input type="text"
placeholder="Username"
onChange={e => changeUsername(e.target.value)}
value={username}></input>
<input type="text"
placeholder="Password"
onChange={e => changePassword(e.target.value)}
value={password}></input>
<Button onClick={() => loginRequest(username, password)}
className='withdraw-deposit-button'>Login/Deposit</Button>
</Card>
);
}
export default LoginPage;
`

how to conditionally render the respond from an api

I want to get input from a user and compare it with the response I am getting from API, and conditionally render the information if it match or just show a sorry message,(the API only contain 1 set of a data object including 4 value) let me know what am I missing.
here is my code
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatInput, setVatInput] = useState("");
const [responseVatState, setResponseVatState] = useState("");
const [responseCountryCodeState, setResponseCountryCodeState] = useState("");
const [result, setResult] = useState(false);
const handelVatState = (event) => {
setVatInput(event.target.value);
};
const closeModalHandler = () => {
setResult(false);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("Some URL")
.then((response) => {
setResponseVatState(response.data.response.data.VATNumber);
setResponseCountryCodeState(response.data.CountryCode);
})
.catch((error) => {
console.log(error);
});
};
const inputCountryCode = vatInput.substring(0, 2);
const inputVatCode = vatInput.substring(2);
if (
inputCountryCode === responseCountryCodeState &&
inputVatCode === responseVatState
) {
setResult(true);
} else {
setResult(false);
}
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
<label className="text-muted">Result : </label>
{result ? (
<div>{vatInput}</div>
) : (
<div clicked={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
)}
</div>
);
}
export default Form;
and the error is
react-dom.development.js:14997 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
so I get the input from the user and set it with hooks, then with Axios call get my data, then I split the string with
const inputCountryCode = vatInput.substring(0, 2);
const inputVatCode = vatInput.substring(2);
to compare with the input I have, if it's the same then render the data if not just render the sorry message
You have a couple of issues, the main of which resulting in Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. is due to an infinite loop of component re-rendering, which you force by setting state directly in the function body.
More specifically, this code:
if (
inputCountryCode === responseCountryCodeState &&
inputVatCode === responseVatState
) {
setResult(true);
} else {
setResult(false);
}
force react to re-evaluate the component because you're changing its state by using setResult. When react starts rendering the new body it yet again encounters setResult which results in a new update and re-render which, as you see, leads to a never-ending loop.
Furthermore, you don't need to save the request response to the component state at all, as it is relevant just for the calculation, which is needed only in the form submit handler itself. So, you should ditch the
const [responseVatState, setResponseVatState] = useState("");
const [responseCountryCodeState, setResponseCountryCodeState] = useState("");
state variables altogether. The only state you need except the input value is the validation result.
Also, you have a typo: setResponseVatState(response.data.response.data.VATNumber); should be setResponseVatState(response.data.VATNumber);.
Try this:
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatValue, setVatValue] = useState("");
const [isVatValid, setIsVatValid] = useState(false);
const handelVatState = (event) => {
setVatValue(event.target.value);
};
const closeModalHandler = () => {
setIsVatValid(false);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("[URL]")
.then((response) => {
const inputCountryCode = vatValue.substring(0, 2);
const inputVatCode = vatValue.substring(2);
const { VATNumber, CountryCode } = response.data;
if (inputCountryCode === CountryCode && inputVatCode === VATNumber) {
setIsVatValid(true);
}
else {
setIsVatValid(false);
}
})
.catch((error) => {
console.log(error);
});
};
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
<label className="text-muted">Result : </label>
{isVatValid ? (
<div>{vatValue}</div>
) : (
<div clicked={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
)}
</div>
);
}
export default Form;
Also, I suppose <div clicked={closeModalHandler}> should be <div onClick={closeModalHandler}>?
EDIT:
Here is your solution after comments:
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatValue, setVatValue] = useState("");
const [isVatValid, setIsVatValid] = useState(null);
const handelVatState = (event) => {
setVatValue(event.target.value);
};
const closeModalHandler = () => {
setIsVatValid(null);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("https://vat.erply.com/numbers?vatNumber=BG999999999")
.then((response) => {
const inputCountryCode = vatValue.substring(0, 2);
const inputVatCode = vatValue.substring(2);
const { VATNumber, CountryCode } = response.data;
if (inputCountryCode === CountryCode && inputVatCode === VATNumber) {
setIsVatValid(true);
}
else {
setIsVatValid(false);
}
})
.catch((error) => {
console.log(error);
});
};
const getResultRepresentation = () => {
if (isVatValid === null) {
return null;
}
if (isVatValid) {
return (
<>
<label className="text-muted">Result: </label>
<div>{vatValue}</div>
</>
);
}
else {
return (
<div onClick={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
);
}
}
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
value={vatValue} // <= missing
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
{getResultRepresentation()}
</div>
);
}
export default Form;
And here is a CodeSandbox to test it out.

React - calling the same function from separate onSubmit and onClick events

I am having an issue with calling an async function in React to an outside API where I'm looking to call the function from two separate events, an onSubmit of a form, and an onClick from the results of a separate search.
I have the following React components:
App.js:
import React, { useState, useEffect } from 'react'
import './App.css';
import Ticker from './compoments/Ticker'
import SearchTicker from './compoments/SearchTicker'
import TickerResults from './compoments/TickerResults'
import SearchCompanyProfile from './compoments/SearchCompanyProfile';
function App() {
const [company, setCompany] = useState([])
const [price, setPrice] = useState([])
const [symbol, setSymbol] = useState([])
async function fetchCompanyProfile(e) {
let company = '';
if(e.target.target.ticker.value){
company = e.target.ticker.value
} else {
company = e.target.innerHTML;
}
console.log(company)
e.preventDefault()
e.target.reset()
const companyData = await fetch(https://someapicompany.com/profile/${company}?apikey=xyz)
.then(res => res.json())
.then(data => data)
setCompany({data: companyData})
}
console.log(company.data)
return (
<div className="App">
<SearchTicker getTicker={fetchTicker}/>
<SearchCompanyProfile getCompanyProfile={fetchCompanyProfile}/>
<TickerResults ticker={symbol} getTicker={fetchCompanyProfile}/>
<CompanyProfile company={company}/>
</div>
);
}
export default App;
SearchCompanyProfile.js
import React from 'react'
const SearchCompanyProfile = (props) => {
return (
<div>
<form onSubmit={props.getCompanyProfile} >
<input type="text" name="ticker" placeholder="Enter Ticker..."/>
<button value="Search" type="submit">
Search
</button>
</form>
</div>
)
}
export default SearchCompanyProfile;
SearchTicker.js
import React from 'react'
const SearchTicker = (props) => {
return (
<div>
<form onSubmit={props.getTicker}>
<input type="text" name="symbol" placeholder="Enter Compamy Name..."/>
<button value="Search" type="submit">
Search
</button>
</form>
</div>
)
}
export default SearchTicker;
TickerResults.js
import React from 'react'
const TickerResults = (props) => {
console.log(props)
return (
<div>
{props.ticker.data && props.ticker.data.map(x => <p name="ticker" value={x.symbol} onClick={props.getTicker}>{x.symbol}</p>)}
</div>
)
}
export default TickerResults;
My goal is to call the async function fetchCompanyProfile from both submitting the SearchCompanyProfile form and clicking on the ticker from the tickerResults component.
I have tried both:
async function fetchCompanyProfile(e) {
let company = '';
if(e.target.ticker.value){
company = e.target.ticker.value;
} else {
company = e.target.innerHTML;
}
console.log(company)
e.preventDefault()
e.target.reset()
const companyData = await fetch(https://someapicompany.com/profile/${company}?apikey=xyz)
.then(res => res.json())
.then(data => data)
setCompany({data: companyData})
}
console.log(company.data)
-and-
async function fetchCompanyProfile(e) {
let company = '';
if(e.target.innerHTML){
company = e.target.innerHTML;
} else {
company = e.target.ticker.value;
}
console.log(company)
e.preventDefault()
e.target.reset()
const companyData = await fetch(https://someapicompany.com/profile/${company}?apikey=xyz)
.then(res => res.json())
.then(data => data)
setCompany({data: companyData})
}
console.log(company.data)
Under both instances it works if the correct event happens in the right order but in the first case, I run into "value cannot be run on undefined" if I use the onClick event through tickerResults component, and in the second case, the innerHTML is just the form inputs from the the searchCompanyProfile form so the fetch is not called with the correct value.
I realize this is a long winded question but any suggestions would be appreciated.
Thy this, you need to call setCompany({ data }) inside of then because it's promise
function App() {
const [company, setCompany] = useState([]);
const [price, setPrice] = useState([]);
const [symbol, setSymbol] = useState([]);
function fetchCompanyProfile(e) {
let company = '';
if(e.target.target.ticker.value){
company = e.target.ticker.value
} else {
company = e.target.innerHTML;
}
console.log(company);
e.preventDefault();
e.target.reset();
fetch('https://someapicompany.com/profile/${company}?apikey=xyz')
.then(res => res.json())
.then(data => {
setCompany({ data })
});
}
console.log(company.data)
return (
<div className="App">
<SearchTicker/>
<SearchCompanyProfile getCompanyProfile={fetchCompanyProfile}/>
<TickerResults ticker={symbol} getTicker={fetchCompanyProfile}/>
<CompanyProfile company={company}/>
</div>
);
}
Here's the async/await solution:
async function fetchCompanyProfile(e) {
let company = '';
if(e.target.innerHTML){
company = e.target.innerHTML;
} else {
company = e.target.ticker.value;
}
console.log(company)
e.preventDefault()
e.target.reset()
let companyData = await fetch(https://someapicompany.com/profile/${company}?apikey=xyz)
setCompany({data: companyData.json()})
}

React repeated onInput handlers in components

I have a react component that looks like the one given below.
The form inputs are handled using the onInputChange function and form submit is handled by onFormSubmit
function RegisterForm() {
// formData stores all the register form inputs.
const [formData, setFormData] = useState(registerDefault);
const [errors, posting, postData] = useDataPoster();
function onInputChange(event: ChangeEvent<HTMLInputElement>) {
let update = { [event.target.name]: event.target.value };
setFormData(oldForm => Object.assign(oldForm, update));
}
function onFormSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const onSuccess: AxiosResponseHandler = response => {
setFormData(Object.assign(formData, response.data));
};
postData("/api/register", formData, onSuccess);
}
return (
<form onSubmit={onFormSubmit}>
<FormTextInput
name="full_name"
label="Name"
errors={errors.full_name}
onChange={onInputChange}
/>
<FormTextInput
name="email"
label="Email address"
type="email"
errors={errors.email}
onChange={onInputChange}
/>
<button type="submit" className="theme-btn submit" disabled={posting}>
{posting && <span className="fas fa-spin fa-circle-notch"></span>}
Create
</button>
</form>
);
}
My app has more than 50 similar forms and I wonder if I have to copy paste these two functions on all the other forms. onInputChange won't be changing a bit and the url is the only variable in onFormSubmit.
I am thinking of a class based approach with setFormData and postData as properties and the functions in question as class methods. But in that case, I have to bind the handlers with the class instance, so that handlers have a valid this instance.
Is there any other way to do this? How would you avoid the repetition of these two code blocks in all the form components?
Thanks
you could create a custom hook, something like this:
const [formState, setFormState] = useFormStateHandler({name: ''})
<input value={formState.name} onChange={event => setFormState(event, 'name')} />
where the definition looks like this:
export default function useFormStateHandler(initialState) {
const [state, setState] = useState(initialState)
const updater = (event, name) => {
setState({...state, [name]: event.target.value})
}
return [state, updater]
}
Create an HOC to inject input handlers to the form components with added params for url.
function RegisterForm(props) {
// specific function
const specific = () => {
const formData = props.formData; // use passed state values
// use form data
}
}
function withInputHandlers(Component, params) {
return function(props) {
// states
function onInputChange(...) {...}
function onFormSubmit(...) {
// use params.url when submitting
postData(params.url, formData, onSuccess);
}
// inject input handlers to component and state values
return (
<Component {...props} formData={formData} onChange={onInputChange} onSubmit={onFormSubmit} />
);
}
}
// Usage
const EnhancedRegisterForm = withInputHandlers(
RegisterForm,
{ url: 'register_url' } // params
);
const EnhancedSurveyForm = withInputHandlers(
Survey,
{ url: 'survey_url' } // params
)
This change may help you
function RegisterForm() {
// formData stores all the register form inputs.
const [formData, setFormData] = useState(registerDefault);
const [errors, posting, postData] = useDataPoster();
const onInputChange = name => event => {
let update = { [name]: event.target.value };
setFormData(oldForm => Object.assign(oldForm, update));
}
const onFormSubmit = url => event =>{
event.preventDefault();
const onSuccess: AxiosResponseHandler = response => {
setFormData(Object.assign(formData, response.data));
};
postData(url, formData, onSuccess);
}
return (
<form onSubmit={onFormSubmit("/api/register")}>
<FormTextInput
name="full_name"
label="Name"
errors={errors.full_name}
onChange={onInputChange("full_name")}
/>
<FormTextInput
name="email"
label="Email address"
type="email"
errors={errors.email}
onChange={onInputChange("email")}
/>
<button type="submit" className="theme-btn submit" disabled={posting}>
{posting && <span className="fas fa-spin fa-circle-notch"></span>}
Create
</button>
</form>
);
}

React Axios Input Undefined

I must post {input} data to http://localhost:4000/prediction with Axios. But {input} turns undefined.
I am using const instead of class Main extends component. onChange, it sets form data.
const Main = ({ value, suggestions, auth: { user } }) => {
const [formData, setFormData] = useState("");
const [messages, setMessages] = useState([]);
const { input } = formData;
const onChange = e => setFormData(e.target.value);
const onSubmit = event => {
event.preventDefault();
setMessages(prevMsgs => [...prevMsgs, formData]);
console.log({ input });
Axios post.
axios
.post(
`http://localhost:4000/prediction`,
{ input },
{ crossdomain: true }
)
.then(res => {
console.log(res.data);
//setMessages(prevMsgs => [...prevMsgs, formData]);
})
.catch(error => {
console.log(error.message);
});
};
Return (form) with onSubmit, onChange.
return (
<div className="true">
<br />
<form noValidate onSubmit={e => onSubmit(e)}>
<div className="input-group mb-3">
<input
name="input"
type="text"
className="form-control"
placeholder="Type text"
onChange={e => onChange(e)}
/>
)}
<div className="input-group-append">
<button className="btn btn-outline-secondary">Send</button>
</div>
</div>
</form>
</div>
);
};
As I have mentioned in the comment section formData is a string as I see which does not have a property called input what you try to destructure and that's why it is undefined always.
If you really need that format for axios then you can try change the structure of formData with useState as the following first:
const [formData, setFormData] = useState({input: null});
Then maybe you can try updating as:
const onChange = e => setFormData({input: e.target.value});
I hope that helps!

Categories