I have created a custom react hook to basically validate a user input field and dispatch a action to verify using some 3rd party api.
Now, now i have two fields which basically does the same thing to use that custom hook, verify and return result. I am using the same hook with 2 intances.
Now, everytime i fill and verify using the hook it works fine, but it also updates the other field, i have changed the name everything but same thing is happening.
Let me share some code.
export default function useVerifier(inputFieldValue, cT) {
const [data, setData] = useState([]);
const [isVerified, setIsVerified] = useState(false)
const [toolTip, setTooltip] = useState({ message: '', type: '' })
const {
details
} = useSelector((state) => state?.profile);
const dispatch = useDispatch()
const checkIFValid = () => {
for (var key in details) {
if (has(details[key][0], 'isValid')) {
setIsVerified(true)
setTooltip(validationMessgage['VALID'])
} else {
setIsVerified(false)
setTooltip(validationMessgage['NOT_VALID'])
}
}
}
useEffect(() => {
checkIFValid()
}, [details])
useEffect(() => {
let isVer = false
setTooltip(validationMessgage.NOT_VALID)
setIsVerified(isVer)
if (inputFieldValue != undefined && inputFieldValue.length === 10) {
const request = {
fieldNumber: [inputFieldValue.toUpperCase()],
};
dispatch(callVerify(request, cT));
}
checkIFValid()
setData(inputFieldValue)
// setIsVerified(isVer);
}, [inputFieldValue, cT]);
return [data, isVerified, toolTip];
}
So the details field which i am extracting from profile, basically consists of array of object, which have isValid key. If that key is true, i set the verifiy flag to be true.
The details field is working and storing data fine in redux store.
Now the hook i am using in one component like this.
const [proposerData, isProposerVerified, proposerToolTips] = usePanVerifier(watchNumber.Number, 'proposer')
const [panddData, isVerified, toolTips] = usePanVerifier(watchInsuredNumber.insuredNumber, 'insurer')
Now the watch here is from react hook form. So the problem is that even both hooks have different parameter, they are called simultaneously, so both the verify (isProposerVerified and isVerified) is getting verified at the same time, even when user have not entered anything in number input field.
I hope i have explained it correctly. Please help. Stuck big time in this.
Related
I have a custom hook to redirect users to edit page. On index page I can duplicate items and delete. I can redirect users after duplicate, but the problem is when I delete an item, this custom hook redirects users to edit page again. So I need to find a way to make it work conditionally.
Custom hook:
export default function useDuplicateItem(url: string) {
const { sendRequest: duplicate } = useHttpRequest();
const duplicateItem = useCallback(
(data) => {
duplicate([
{
url: `/api/server/${url}`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data,
},
]);
},
[duplicate, url]
);
useRedirectEditPage(url); // This causes the problem
return duplicateItem;
}
index page:
const duplicateItem = useDuplicateItem('documents');
// custom hook rendered here, which is not correct. I want to run it when duplicate function runs.
const duplicate = useCallback(() => {
const data = {
name: copiedName,
sources: singleDocument?.sources,
document: singleDocument?.document,
tool: singleDocument?.tool,
access: singleDocument?.access,
};
duplicateItem(data);
}, [copiedName, duplicateItem, singleDocument]);
useRedirectEditPage:
export default function useRedirectEditPage(slug: string) {
const { saveResponses, setSaveResponses, setHeaderStates } =
useAdminContext();
const router = useRouter();
useEffect(() => {
const statusCodes: number[] = [];
let id;
saveResponses.forEach((value) => {
statusCodes.push(value?.status);
id = value?.id;
});
if (statusCodes.length && id) {
if (statusCodes.includes(404)) {
setHeaderStates((prev) => ({
...prev,
canBeSaved: false,
}));
} else {
router.push(`/admin/${slug}/edit/${id}`);
setSaveResponses(new Map());
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [saveResponses, router, setSaveResponses]);
}
saveResponses state is coming after I make any request to server, and I am able to get id to redirect users. I use new Map() to set data inside saveResponses.
From the react docs:
Don’t call Hooks inside loops, conditions, or nested functions.
Instead, always use Hooks at the top level of your React function,
before any early returns. By following this rule, you ensure that
Hooks are called in the same order each time a component renders.
That’s what allows React to correctly preserve the state of Hooks
between multiple useState and useEffect calls. (If you’re curious,
we’ll explain this in depth below.)
React relies on the order in which Hooks are called to know which setState corresponds to which state, calling them inside a condition will mess up the previous mechanism.
I would recommend to read the following: https://reactjs.org/docs/hooks-rules.html#explanation
I am using react(next.js) to build a simple application. In this particular case, I'm trying to create a form in add and edit mode. If the get parameter contains edit, the form makes some api calls else it makes some different api calls. I'm passing the get paramaters as props but that doesn't seem to work.
Here's my code from the page:
const [formMode, setFormMode] = useState(h.form.FORM_MODE.ADD);
const [selectedContactId, setSelectedContactId] = useState("");
useEffect(() => {
const contact_id = h.findGetParameter("contact_id");
setSelectedContactId(contact_id);
const form_mode = h.findGetParameter("form_mode");
setFormMode(form_mode);
console.log(form_mode);
console.log(contact_id);
}, []);
return (
<div >
<Header />
<Body>
<div className="container projects-container">
<div className="mb-5 projects-title">
<h1> Create Link</h1>
</div>
<CreateMyForm
setLoading={setLoading}
formMode={formMode}
setFormMode={setFormMode}
selectedContactId={selectedContactId}
/>
</div>
</Body>
<Footer />
</div>
I'm taking get parameters contact_id and form_mode from the URL, setting them into in useState(which is working fine and being printed in console), and then passing them to the CreateMyForm component as props. It doesn't seem to be received in the component. Below is part of my code from the component which requires these parameters.
const { setLoading, formMode, setFormMode, selectedContactId } = props;
useEffect(() => {
// const contact_id = h.findGetParameter("contact_id");
// console.log("contact id = ", contact_id);
// setSelectedContactId(contact_id);
// const form_mode = h.findGetParameter("form_mode");
// setFormMode(form_mode);
// // console.log(contact_id);
// console.log("latest = ", form_mode);
// console.log("latest = ", selectedContactId);
(async () => {
if (h.cmpStr(formMode, h.form.FORM_MODE.EDIT)) {
const selectedContactRes = await api.contact.findById(
{ contact_id: selectedContactId },
{},
false
);
console.log("Her's me agains: ", selectedContactRes.data);
setSelectedContact(selectedContactRes.data.contact);
}
let projectApiRes = await api.project.contentFindAll({}, {}, false);
if (h.cmpStr(projectApiRes.status, "ok")) {
if (
projectApiRes.data.projects &&
projectApiRes.data.projects.length > 0
) {
let projects = handleProjectOptionList(projectApiRes.data.projects);
setProjects(projectApiRes.data.projects);
setProjectList(projects);
}
}
if (h.cmpStr(formMode, h.form.FORM_MODE.ADD)) {
let contactApiRes = await api.contact.findAll({}, {}, false);
if (h.cmpStr(contactApiRes.status, "ok")) {
if (
contactApiRes.data.contacts &&
contactApiRes.data.contacts.length > 0
) {
let contacts = handleContactOptionList(contactApiRes.data.contacts);
console.log("I am coming here");
setContactList(contacts);
}
}
}
})();
}, []);
I've even tried to try to capture the get parameters in the component but that doesn't seem to work either.
I'm guessing the problem is with the async API calls but I am not sure how to fix it. Any help please.
Thanks in advance
Your useEffect() function is running only when the component loads.
You have to call all the code inside of useEffect everytime contact_id or form_mode changes so I suggest you pass these as observable arguments to useEffect as follows
useEffect(()=>{
// All your runnable code
}, [contact_id, form_mode])
Now all your code inside will run each time contact_id or form_mode is even slightly changed
Also, I would recommend you to wrap all your awaits in a try-catch block so you can test out how well the API works
I have the next hook in my react js application:
const {
data,
loading
} = fetchData(info)({
variables: {
id: myId,
},
fetchPolicy: 'no-cache',
});
//
const fetchData = (info) => {
if (a > 1) {
return useGetCars;
}
return useGetColors;
};
The issue appear in the first render when the myId is empty, but it is required. Due this fact i get an error from the server.
Question: How to create a condition for the hook above to be able to run it only when the myId is not empty?
Use the effect hook:
useEffect(() => {
if (myId) {
fetchData() // after this, you can set the required states.
}
}, [myId])
I have read several cases on stackoverflow regarding missing dependencies in useEffect:
example :
How to fix missing dependency warning when using useEffect React Hook?
Now the case I have is, I use useEffect for pagination:
Here's the source code:
react-router-dom configuration
{ path: "/note", name: "My Note", exact: true, Component: Note },
Note Component
const Note = (props) => {
const getQueryParams = () => {
return window.location.search.replace("?", "").split("&").reduce((r, e) => ((r[e.split("=")[0]] = decodeURIComponent(e.split("=")[1])), r),
{}
);
};
const MySwal = withReactContent(Swal);
const history = useHistory();
// Get Queries On URL
const { page: paramsPage, "per-page": paramsPerPage } = getQueryParams();
// Queries as state
const [queryPage, setQueryPage] = useState(
paramsPage === undefined ? 1 : paramsPage
);
const [queryPerPage, setQueryPerPage] = useState(
paramsPerPage === undefined ? 10 : paramsPerPage
);
// Hold Data Records as state
const [notes, setNotes] = useState({
loading: false,
data: [],
totalData: 0,
});
useEffect(() => {
console.log(queryPage, queryPerPage);
setNotes({
...notes,
loading: true,
});
// Fetching data from API
NoteDataService.getAll(queryPage, queryPerPage)
.then((response) => {
setNotes({
loading: false,
data: response.data,
totalData: parseInt(response.headers["x-pagination-total-count"]),
});
return true;
})
.catch((e) => {
MySwal.fire({
title: e.response.status + " | " + e.response.statusText,
text: e.response.data,
});
});
return false;
}, [queryPage, queryPerPage]);
const { loading, data, totalData } = notes;
...
So there are two problems here:
There is a warning React Hook use Effect has missing dependencies: 'MySwal' and 'notes'. Either include them or remove the dependency array. You can also do a functional update 'setNotes (n => ...)' if you only need 'notes' in the 'setNotes' call. If I add notes and MySwal as dependencies, it gives me a continuous loop.
When I access the "note" page, the Note component will be rendered.
Then, with pagination: / note? Page = 2 & per-page = 10, it went perfectly.
However, when returning to "/ note" the page does not experience a re-render.
Strangely, if a route like this / note? Page = 1 & per-page = 10, returns perfectly.
Does my useEffect not run after pagination?
First of all, move your API call inside of useEffect. After your data is fetched, then you can change the state.
useEffect(() => {
//Fetch the data here
//setState here
},[]) //if this array is empty, you make the api call just once, when the `component mounts`
Second Argument of useEffect is a dependancy array, if you don't pass it, your useEffect will trigger in every render and update, which is not good. If you parss an empty array, then it makes just one call, if you pass a value, then react renders only if the passed value is changed.
I get the data from my database like so:
const clicked = props.clicked;
const [allEmployees, setAllEmployees] = useState([]);
const [list, setList] = useState([]);
useEffect(()=>{ //getting employees
Axios.get(
PATH + `/${employees}`
).then((data) => {
setAllEmployees(data.data);
});
},[]);
useEffect(()=>{ //getting list of employees who should be selected
Axios.get(
PATH + `/${list}`
).then((data) => {
setList(data.data);
setList(
allEmployees.map(t=>{
if(list.includes(t.id)){
return{
select: true,
id: t.id,
name: t.name
}
}else{
return{
select: false,
id: t.id,
name: t.name
}
}
})
)
console.log(allEmployees);// <<
console.log(list);// <<
});
},[clicked]);
My problem is that the fist time I click on the buttons, activating clicked.props, both console.log() show empty arrays. After the second click and on, they work and show the arrays. I'm guessing I need to update them in a better way, but don't know how. (I'm trying to show the data but it really shows nothing on the first click of the button).
this is because they actually are empty arrays at first. Axios sends an async request to the server, fetching data. The state hooks are rendered right after the first page render, so basically at the same time. The data from the server will return when it returns. (as it is a promise). Once the data returns as a promise, you can resolve it and add the resolved data to the state.
It also seems like you're trying to set the list state twice with different data in the second useEffect. You are using the fetched data from axios first, and using the the data from the first useEffect after (allEmployees), it's kind of hard for me to really understand your thought process here.
the reason why you get the console.logs return empty on first click is because of the way useState works , when you set state you'll have to wait until the component reaload to see the new values of your state . so when you first click the state was empty so you get empty logs but on the second click you get none empty values but actually their just the new values from your first click :
const [value,setValue] = useState(0)
useEffect(()=>{ //getting list of employees who should be selected
Axios.get(PATH + `/${list}` )
.then((data) => {
setValue(value+1 )
});
console.log(value);//this will output 1
},[]);
useEffect(()=>{ //getting list of employees who should be selected
console.log(value);//this will output 2 on the next component render
},[value]);
so in your case you can check if list and employes are not empty if so return null :
const clicked = props.clicked;
const [allEmployees, setAllEmployees] = useState([]);
const [list, setList] = useState([]);
useEffect(()=>{ //getting employees
Axios.get(
PATH + `/${employees}`
).then((data) => {
setAllEmployees(data.data);
});
},[]);
useEffect(()=>{ //getting list of employees who should be selected
Axios.get(
PATH + `/${list}`
).then((data) => {
setList(
allEmployees.map(t=>{
if(list.includes(t.id)){
return{
select: true,
id: t.id,
name: t.name
}
}else{
return{
select: false,
id: t.id,
name: t.name
}
}
})
)
console.log(allEmployees);// <<
console.log(list);// <<
});
},[clicked]);
if(!allEmployees.length || !list.length) return null
// after this line you're garanted that allEmployees and list are not empty
render <div>{list.map(your implemntation)}<div>