I made a hook useSendFormData , when i use it i get Invalid hook call.
Hook takes
data from SubmitForm
url:string,
method: post or put
success: success message if it was success
id: not required but if item has id i is added to api call.
auth default false
Problem is it loses data on renders I don’t know how to describe it better so I made console.log ss
As you can see on second call I get data but later it’s gone…
My code for this custom hook:
const sendFormData = async ({ formData, url, method, success, id, auth = false, }) => { const setPartData = (partialData) => setData({ ...data, ...partialData }); try { let response; if (method === "post") { response = await axios.post( `${SERVER_API}api/v1/${url}/${id ?? ""}`, formData ); } else if (method === "put") { response = auth ? await fetchContext.authAxios.post( `${SERVER_API}api/v1/${url}/${id ?? ""}`, formData ) : await axios.post( `${SERVER_API}api/v1/${url}/${id ?? ""}`, formData ); } setPartData({ data: response.data, loading: false, success, error: null, }); } catch (err) { const { data } = err.response; setPartData({ error: data.error, success: null, loading: false, }); } return data; }; return { sendFormData, }; };
And where is use it , it takes data from SubmitForm and make api call with it,as you can see in ss i get there undefined:
const { sendFormData } = useSendFormData() const handleForm = async (info) => { // here you have your response.data returned const data = await sendFormData({ formData: info, url: "auth/forgot-password", method: "post", success: "A password reset message has been sent to your email", }); console.log(data); reset(); };
If you could help that would be awesome. If you have any optimatizaion hints for this hook please tell me.Thanks for your time.
Edit: Edit hook but doesnt return data value at the end
Advertisement
Answer
TL;DR
const setPartData = (partialData) => setData({ ...data, ...partialData });
should be changed to
const setPartData = (partialData) => setData(data => ({ ...data, ...partialData }));
Explanation
setState
callback can take either the new state (what you have done), or another callback (what should be done). You should pass a callback like so, to make sure that multiple calls of setPartialData(someData)
within sendFormData
uses the latest data
state to update itself (combining partialData
). This approach is necessary as const sendFormData = () => {}
is declared, the data
variable used within is whatever data
was outside the function block (from const [data, setData] = useState()
). This data
is not updated as sendFormData()
runs, instead only on the end of a component render cycle.
In other words,
const [data, setData] = useState(initialData); const sendFormData = () => { const setPartData = (partialData) => setData({ ...data, ...partialData }); setPartData(data1); // combines with initialData setPartData(data2); // combines with initialData as well }
Note that where you declare const setPartData
does not matter, as long as it has access to the data
scope.
On the other hand,
const setPartData = (partialData) => setData(data => ({ ...data, ...partialData }));
uses the latest data
state when setData()
needs to run, by accessing it through the internal callback.
Since the hook you built can return the data
state, once it is updated correctly, you can now use it like this in a better manner.
// App.js const {data, sendFormData} = useSendFormData(); const sendAction = async (arg) => await sendFormData(arg); return <> <button onClick={sendAction}>send data</button> { data.success || someCondition ? data : null } </>
It’s probably not a good idea to reinvent the wheel when this swr library exists, but I commend your effort in trying to make code DRY.