I am new to React so please forgive me if this issue is entry-level to you.
Basically, I want to do the thing as the title goes,
here I have a function designed for this purpose:
function saveAndUpdate() {
// data processing and wrap up the finalized data, lets call it newData
useEffect(() => {
async function loadData() {
try {
await axios.put(API/updateEntry,{objToUpdate: newData}).then(() => {
const { loading, error, data } = axios.get(API/dataForTheTable)
callback(data);
});
} catch (error) {
console.error(error);
}
}
loadData();
}, []);
}
callback is a function in parent component in order to update the state related to the data immediately after the data is updated.
const handleCallback = (data) => {
setInfo(data);
}
And by running this, I get the classic error which I still do not fully understand:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
Any help will be appreciated, thank you!
Seems that what you're doing is creating a hook (saveAndUpdate) and trying to access to It somewhere (for example, inside another hook or function).
Althought, a hook to update data (with axios.put, where you need to pass an argument; the data for the update) is not a good approach, as an UseEffect hook will run when the component is mounted, not when the user clicks on a button or whatever. For example, it would be better if you use this for gathering data (get request) from your backend when the component mounts.
Anyway this is a way you could do what you want to achieve, using the useCallback. I've created a button that fires the function and pass some data (just you can see the expected behaviour):
In the saveAndUpdate hook (name it as useSaveAndUpdate, just a convention for hooks names):
import { useEffect, useState } from "react";
export default function useSaveAndUpdate(dataUpdate) {
const [data, setData] = useState(null);
const [updating, setUpdating] = useState(false);
useEffect(() => {
if (updating) {
const req = function () {
// the put request (dataUpdate will be the data we use for the request)...
// for this i'm just going to set data to the argument we've passed
// the idea is to setData to the returned data from the server
setData(dataUpdate);
setUpdating(false);
// setting updating to false, if there's a re-render it won't do the request again
};
req();
}
}, [updating, dataUpdate]);
return [data, updating, setUpdating];
}
In the parent component:
import { useState, useCallback } from "react";
import useSaveAndUpdate from './useSaveAndUpdate'
const parent = () => {
const [updateTarget,setUpdateTarget = useState(null)
const [data,updating,setUpdating] = useSaveAndUpdate(updateTarget)
const updateFunction = useCallback((updateData) => {
setUpdating(true)
//
// ^ Setting updating to true will fire the request inside useSaveAndUpdate when re-render
//
setUpdateTarget(updateData)
// Setting the data for the update (this data will be used in the put request)
},[setUpdating,setUpdateTarget])
return (
<h3>{loading?'loading...':'not loading'}</h3>
<h3>{data?data.name:'data is null'}</h3>
{/*data will be updated when button clicked, so if it's a table just do data.map()...*/}
<button onClick={() => updateFunction({name:'jhon'})}>Click me to update</button>
)
}
So at first render, It will outpout that It's not loading and data is null. When click It'll say loading for a sec (the time the request lasts), and the data will be displayed ('jhon').