javascript - Trimiterea unui formular cu date folosind un hook React personalizat
Am probleme să înțeleg asta. Vreau să creez un hook care să fie apelat pentru a trimite un formular folosind fetch.
Acesta este ceea ce am acum. Componenta care deține formularul:
const MyForm = (): ReactElement => {
const [status, data] = useSubmitForm('https://myurl-me/', someData);
return <>
<div className='Feedback-form'>
<div className='body'>
<form>
<input type='text' name='username' placeholder='name' required />
<input type='email' name='email' placeholder='email' required />
<button className='submit-feedback-button' type='button'>Send feedback</button>
</form>
</div>
</div>
</>
}
Hook-ul personalizat:
import { useState, useEffect } from 'react';
const useSubmitForm = (url: string, data: URLSearchParams): [string, []] => {
const [status, setStatus] = useState<string>('idle');
const [responseData, setData] = useState<[]>([]);
useEffect(() => {
if (!url) return;
const fetchData = async () => {
setStatus('fetching');
const response = await fetch(url, {
method: 'POST',
headers: {
'Accept': 'text/html',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: data
});
const data = await response.json();
setData(data);
setStatus('fetched');
};
fetchData();
}, [url]);
return [status, responseData];
};
export default useSubmitForm;
Problema mea este că cred că acest hook este apelat imediat. Cum pot să fac acest hook și să-l apelez într-un mod în care să fie apelat doar atunci când formularul este trimis și toate datele de care am nevoie pentru a fi incluse în corpul cererii sunt acolo?
Răspuns #1
Ai dreptate, efectul rulează o singură dată când componenta se montează și deoarece url este adevărat, sare peste returnarea timpurie și invocă fetchData.
Cum fac acest hook și îl apelez într-un mod în care să fie apelat doar atunci când formularul este trimis și toate datele de care am nevoie pentru a fi trimise în corpul cererii sunt acolo pentru a fi incluse?
Trebuie să returnezi și o funcție pentru ca componenta să o apeleze și să transmită valorile câmpurilor formularului. Cred că ai câteva opțiuni de bază.
- Convertește câmpurile formularului pentru a fi inputuri controlate și stochează starea câmpurilor în componentă și apelează o funcție „fetch” returnată de hook-ul
useSubmitForm. - Returnează un handler
onSubmitdinuseSubmitFormpentru a-l atașa la elementul tăuform. Totuși, handler-ulonSubmitva trebui să știe ce câmpuri să acceseze din evenimentulonSubmit, deci trecerea unui array de nume de câmpuri către hook (adică un „config”) are sens.
Soluția 1 - Folosește intrări controlate și funcția fetch returnată
Despachetează funcția fetchData din hook-ul useEffect și adaugă-i un parametru de date pentru câmpul formularului. Deoarece fetch și response.json() pot arunca erori/rejectări, ar trebui să înconjori acest bloc cu un try/catch. Returnează funcția personalizată fetchData pentru ca formularul să o poată invoca.
foloseșteTrimiteFormular
const useSubmitForm = (
url: string,
data: URLSearchParams
): [function, string, []] => {
const [status, setStatus] = useState<string>("idle");
const [responseData, setData] = useState<[]>([]);
const fetchData = async (formData) => {
setStatus("fetching");
try {
const response = await fetch(url, {
method: "POST",
headers: {
Accept: "text/html",
"Content-Type": "application/x-www-form-urlencoded"
},
body: JSON.stringify(formData)
});
const data = await response.json();
setData(data);
setStatus("fetched");
} catch (err) {
setData(err);
setStatus("failed");
}
};
return [fetchData, status, responseData];
};
MyForm
const MyForm = (): ReactElement => {
const [fields, setFields] = useState({ // <-- create field state
email: '',
username: '',
});
const [fetchData, status, data] = useSubmitForm(
"https://myurl-me/",
someData
);
useEffect(() => {
// handle successful/failed fetch status and data/error
}, [status, data]);
const changeHandler = (e) => {
const { name, value } = e.target;
setFields((fields) => ({
...fields,
[name]: value
}));
};
const submitHandler = (e) => {
e.preventDefault();
fetchData(fields); // <-- invoke hook fetchData function
};
return (
<div className="Feedback-form">
<div className="body">
<form onSubmit={submitHandler}> // <-- attach submit handler
<input
type="text"
name="username"
placeholder="name"
onChange={changeHandler} // <-- attach change handler
value={fields.username} // <-- pass state
/>
<input
type="email"
name="email"
placeholder="email"
onChange={changeHandler} // <-- attach change handler
value={fields.email} // <-- attach state
/>
<button className="submit-feedback-button" type="submit">
Send feedback
</button>
</form>
</div>
</div>
);
};
Soluția 2 - Returnează un handler onSubmit și transmite un array de câmpuri către useSubmitForm
foloseșteTrimiteFormular
const useSubmitForm = (
url: string,
data: URLSearchParams,
fields: string[],
): [function, string, []] => {
const [status, setStatus] = useState<string>("idle");
const [responseData, setData] = useState<[]>([]);
const fetchData = async (formData) => {
setStatus("fetching");
try {
const response = await fetch(url, {
method: "POST",
headers: {
Accept: "text/html",
"Content-Type": "application/x-www-form-urlencoded"
},
body: JSON.stringify(formData)
});
const data = await response.json();
setData(data);
setStatus("fetched");
} catch (err) {
setData(err);
setStatus("failed");
}
};
const onSubmit = e => {
e.preventDefault();
const formData = fields.reduce((formData, field) => ({
...formData,
[field]: e.target[field].value,
}), {});
fetchData(formData);
}
return [onSubmit, status, responseData];
};
MyForm
const MyForm = (): ReactElement => {
const [onSubmit, status, data] = useSubmitForm(
"https://myurl-me/",
someData,
['email', 'username'] // <-- pass field array
);
useEffect(() => {
// handle successful/failed fetch status and data/error
}, [status, data]);
return (
<div className="Feedback-form">
<div className="body">
<form onSubmit={onSubmit}> // <-- attach submit handler
<input
type="text"
name="username"
placeholder="name"
/>
<input
type="email"
name="email"
placeholder="email"
/>
<button className="submit-feedback-button" type="submit">
Send feedback
</button>
</form>
</div>
</div>
);
};
În opinia mea, a doua soluție este soluția mai curată și necesită mai puține componente consumatoare pentru a fi utilizată.