Reputation: 335
I am new to react and I am facing problem with useState hook. I have one from (using useForm), what I want to achieve is when user click on submit button it will send form data to api and collect a response from api then update state and send that updated state as params in url redirection (props.history.push). The only thing I stuck with is my useState hook is not updating on first click. I found similar questions but that did not solve my problem or one can say I did not understood it very well, pls help.
import React, {useState, useRef, useCallback, useEffect} from 'react'
import {useForm} from 'react-hook-form'
import CreateProcessComponent from '../../components/ProcessComponents/CreateProcess/CreateProcessComponent.jsx'
import {createProcessApiCall} from '../../services/ProcessService'
function postData (dt, err, param){
var new_param = JSON.stringify(param)
const result = createProcessApiCall(new_param)
.then(response => {
dt(response.data.data)
console.log(response.data.data)
})
.catch(e => {
console.log(e)
err(true)
})
}
export default function CreateProcessContainer(props){
const {register, handleSubmit, errors} = useForm()
const [postApiData, setApiData] = useState({})
const [isError, setIsError] = useState(false);
const sendRequest= (data) => {
let prm = {"ProcessName":data.processName,"ProcessDescription":data.processDescription,"Code":"null", "CreatedBy":"null"}
postData(setApiData, setIsError, prm)
props.history.push({pathname:'/test',state:{postApiData}})
}
return (
<main>
{isError && <div>Something went wrong ...</div>}
{ (!isError) ?
<CreateProcessComponent
register={register}
handleSubmit={handleSubmit}
errors={errors}
onSubmit={sendRequest}/>
:
null }
</main>
)
}
in above code CreateProcessComponent is my form. pls let me know what I am doing wrong and what fixes I need to add
Upvotes: 1
Views: 1239
Reputation: 335
Below changes solved my problem, let me know if I can improve the code
import React, {useState, useRef, useCallback, useEffect} from 'react'
import {useForm} from 'react-hook-form'
import CreateProcessComponent from '../../components/ProcessComponents/CreateProcess/CreateProcessComponent.jsx'
import {createProcessApiCall} from '../../services/ProcessService'
export default function CreateProcessContainer(props){
const {register, handleSubmit, errors} = useForm()
const [postApiData, setApiData] = useState({})
const [isError, setIsError] = useState(false);
function postData (dt, err, param){
var new_param = JSON.stringify(param)
createProcessApiCall(new_param)
.then(response => {
dt(response.data.data)
// dt(response.data.data)
console.log(response.data.data)
})
.catch(e => {
console.log(e)
err(true)
})
}
useEffect(()=>{if (Object.keys(postApiData).length > 0)
props.history.push({pathname:'/test',state:postApiData})},[postApiData])
const sendRequest= (data) => {
let prm = {"ProcessName":data.processName,"ProcessDescription":data.processDescription,"Code":"null", "CreatedBy":"null"}
postData(setApiData, setIsError, prm)
}
return (
<main>
{isError && <div>Something went wrong ...</div>}
{ (!isError) ?
<CreateProcessComponent
register={register}
handleSubmit={handleSubmit}
errors={errors}
onSubmit={sendRequest}/>
:
null }
</main>
)
}
Upvotes: 0
Reputation: 202761
sendRequest
is updating state, but you are trying to grab the updated state for the navigation push on the next line. React state updates are asynchronous and the updated postApiData
state value won't be available until at least a render cycle later. postData
is also doing some asynchronous processing but it doesn't return a promise that can be awaited on.
So basically sendRequest
fires off a postData
call, then immediately does the navigation since this function is completely synchronous
const sendRequest= (data) => {
...
postData(setApiData, setIsError, prm) // <-- setApiData updates state
props.history.push({
pathname: '/test',
state: { postApiData } // <-- current state
});
}
sendRequest
an async function and await
a response from postData
in a try/catchcatch
postData
instead of passing state update functionsUnpack the response data and return it
const postData = param => {
const new_param = JSON.stringify(param);
return createProcessApiCall(new_param)
.then((response) => {
return response.data.data;
});
};
Remove the postApiData
state and await the response from postData
.
export default function CreateProcessContainer(props) {
const { register, handleSubmit, errors } = useForm();
const [isError, setIsError] = useState(false);
const sendRequest = async (data) => {
const prm = {
ProcessName: data.processName,
ProcessDescription: data.processDescription,
Code: "null",
CreatedBy: "null"
};
try {
const postApiData = await postData(prm);
history.push({ pathname: "/result", state: { postApiData } });
} catch {
setIsError(true);
}
};
return (
<main>
{isError ? (
<div>Something went wrong ...</div>
) : (
<CreateProcessComponent
register={register}
handleSubmit={handleSubmit}
errors={errors}
onSubmit={sendRequest}
/>
)}
</main>
);
}
sendRequest
Unpack the response data and pass it to and invoke the success callback. Pass the failure callback to the catch block.
const postData = (param, success, failure) => {
const new_param = JSON.stringify(param);
createProcessApiCall("result data from callbacks")
.then((response) => {
success(response.data.data);
})
.catch(failure);
};
Remove the postApiData
state, and pass the param and success/failure callbacks to postData
.
export default function CreateProcessContainer(props) {
const { register, handleSubmit, errors } = useForm();
const [isError, setIsError] = useState(false);
const sendRequest = (data) => {
const prm = {
ProcessName: data.processName,
ProcessDescription: data.processDescription,
Code: "null",
CreatedBy: "null"
};
postData
prm,
(postApiData) => {
history.push({ pathname: "/result", state: { postApiData } });
},
() => setIsError(true)
);
};
return (
<main>
{isError ? (
<div>Something went wrong ...</div>
) : (
<CreateProcessComponent
register={register}
handleSubmit={handleSubmit}
errors={errors}
onSubmit={sendRequest}
/>
)}
</main>
);
}
Upvotes: 1
Reputation: 254
Try this:
const main = useRef(null);
if(main) {}
<main ref={main}>
PS: Haven't sense
// set isMounted to false when we unmount the component
useEffect(() => {
return () => {
isMounted.current = false
}
}, [])
Your component will destroy, for what sets isMounted
?
Upvotes: 1