GraphQL useLazyQuery not working properly when onClick - reactjs

I quite new to GraphQL. I'm trying to use useLazyQuery hook for my onBtnClick function. The button supposed to query and do something everytime on click.
The first time i click on the button the data return is undefined, only second try onwards then it works. This is some sample code below:
const [getItemList, { loading, data}] = useLazyQuery(ItemList, { fetchPolicy: 'network-only' });
const onBtnClick = () => {
getItemList();
if (loading) {
//do something
});
if (data) {
//do another thing
}
}
return (
<div>
<button onClick={onBtnClick}>Something</button>
</div>
)
Could anyone guide me or explain on this?

Cause data in onBtnClick function is not newest data. You need a useEffect to listen data and loading change.
useEffect(() => {
console.log(data, loading)
}, [data, loading])

Related

react-router-dom not updating useParmams() when Link to same path with different url

It renders fine if I click the link in the <MeetNew> component from a different component, but when a <MeetNew> Link is clicked from the <User> component, the page doesn't load correctly.
on the component
const User = () => {
let { id } = useParams()
let res2;
const [userInfo, setUserInfo] = useState({
user: {},
listItems: []
})
const { listItems } = userInfo
useEffect(() => async () => {
try {
if (id) {
const res2 = await axios.get(`/api/listItems/${id}`)
setUserInfo({ listItems: res2.data })
console.log('render')
}
} catch (err) {
console.error(err)
}
}, [id])
return (
...
)
I feel like I'm not using useParams() correctly or useEffect correctly. When I click the link the URL change is correct, but useParams() doesn't re-render or re-mount my component.
I guess you are referring to pages as components? Is it possible that your id param does not change, so your useEffect is not activated, thus there is no loading of items?
The useEffect looks very suspect to me. It doesn't run any logic as part of the effect other than to return an async cleanup function. This probably isn't what you meant to implement.
Refactor the useEffect callback to declare an async function and only invoke it if there is a truthy id dependency value.
useEffect(() => {
const getItems = async () => {
try {
const res2 = await axios.get(`/api/listItems/${id}`);
setUserInfo({ listItems: res2.data });
} catch (err) {
console.error(err);
}
}
if (id) {
getItems();
}
}, [id]);
What I suspect is happening is that you are using react#18 and rendering the app into a React.StrictMode component. In react#18 the StrictMode component intentionally "double-mounts" react components as a way to ensure Reusable State. The returned cleanup function runs and makes the GET request and the state is updated. The only occurs on the initial mounting/render of the component, subsequent renders occur normally. This part is just a hunch though by looking at just your code without testing a running demo.

React Render UI Before Redirect

I am having problem rendering ui before redirect in react. I has a variable is called from api, i want to check if it is 'x' then will redirect. But ui will render before redirect.
Bellow is my code:
useLayoutEffect(() => {
getProfile().then((res) => {
setIsLoading(true);
if (res) {
redirectByUserType(res.data.type); // redirect to /home
}
});
}, []);
I tried using useLayoutEffect but not working.
Please help me, thank you so much.
If you don't want to render until getProfile() has finished, then have a state variable which tracks whether it is finished. If it hasn't finished, return null to render nothing. If it has, return whatever you want to render. I would normally call this state variable loading, but you seem to already have one with that name, who's purpose i don't know. Maybe you can piggy back on that, maybe you need a separate one:
const [ready, setReady] = useState(false);
useEffect(() => {
getProfile().then(res => {
setIsLoading(true);
if(res) {
redirectByUserType(res.data.type);
} else {
setReady(true)
}
});
}, []);
if (!ready) {
return null;
}
return (
<div>Something</div>
);

Can't Update state after fetch request

I'm trying to make an app where I fetch data from a Graphql API, and update state after fetching, but after the successful request I'm getting the data but I can't update state with that data. when I console log the response data it's showing the array with required data but when I update state and console log the state it's showing empty array.
I need to update the state to use it in the component, when i'm doing this it's throwing error that currList is undefined.
here are pictures of code and console.
export default function App() {
const [search, setSeach] = useState("");
const [currList, setCurrList] = useState([]);
const fetchShips = async () => {
const response = await request(gql`
{
ships {
name
home_port
image
}
}
`);
console.log("request response", response.data);
setCurrList(response.data);
console.log("currlist:", currList);
};
useEffect(() => {
fetchShips();
}, [currList]);
const Searchitems = (event) => {
setSeach(event.target.value);
setCurrList(
currList.filter((item) => {
return item.name.includes(search) || item.home_port.includes(search);
})
);
};
return (
<div className="App">
<div className="container">
<header></header>
<div className="body">
<input
type="text"
id="search"
value={search}
onChange={Searchitems}
className="input"
placeholder="Search Ships"
/>
<p>Total Count: {currList.length}</p>
<div className="ships">
{currList.map((item, index) => {
return (
<Ship
key={index}
name={item.name}
port={item.port}
img={item.image}
/>
);
})}
</div>
</div>
</div>
</div>
);
}
the State is Updated just not when the code is running that's why it logs that the state is an empty array, try to console.log it once again and you will see that there is something in the List.
That's normal, everything is happening asynchronously. React will trigger a re-render once your currList is updated and you will get its new value. If you want to listen to this change, you have to use useEffect with currList as a dependency.
useEffect(() => {
console.log("List is updated", currList);
}, [currList]);
The function fetchShips has closure over the currList state variable that blongs to specific render call, so when you log it after calling setCurrList it shows you the previous state not the updated state, you can log it in the useEffect to see the changes.
I had the same problem once. I couldn't find efficient solution but my solution saved me anyway.
I used useRef instead of useState.
Try this:
const currList = useRef([]);
useEffect=(()=>{
const fetchShips = async () => {
const response = await request(gql`
{
ships {
name
home_port
image
}
}
`);
console.log("request response", response.data);
// setCurrList(response.data);
if(response)
currlist.current = response.data
console.log("currlist:", currList);
};
fetchShips()
// Also as far as I know ,you should use useEffect like this
},[])
//... codes
return(
//... codes
currList.current.map(...
)
//... codes
Before using useRef, try to define your fetchShips function inside useEffect so maybe you don't need my solution.
Why is not efficient my solution for your case?
When you want to update your currList data, useRef does not trigger re-render. Even if your data updated, you cannot see it on your screen.
So setCurrList(currList.current) can save you but as I said earlier it may not efficient way.

Undefined redux props when refresh page in react

I'm having trouble getting datas from redux when I refresh my page
const [filters, setFilters] = useState<string[]>(props.filters);
useEffect(() => {
(() => {
if (!_.isEqual(filters, props.filters)) {
setFilters(props.filters);
}
})();
});
My filters are undefined even though when I check redux devtools, there is datas in filters.
I need to trigger an event in my front to display my filters.
Anyone have an idea please ?
Edit:
(if I click any element in my page it load my filters)
If I add a setTimeout on refresh it works but I'm not sure using setTimeout is a solution
useEffect(() => {
setTimeout(() => {
setFilters(props.filters);
}, 1500);
}, []);
May be you just missed returning the inner functions:
Or just call the setFilters like:
useEffect(() => {
if (!_.isEqual(filters, props.filters)) {
setFilters(props.filters);
}
});

running a useEffect after a click even if the url trigger isn't changed

I'm following the nice article "How to fetch data with React Hooks?", in particular the section "ERROR HANDLING WITH REACT HOOKS".
There we have a useEffect that depends on the url where we fetch data from.
fetchData();
}, [url]);
The url is set on a submit from an input, specifying the query string.
<form
onSubmit={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
My problem is that, even if the author defines
The error is just another state initialized with a state hook
and he implements the try/catch block for error handling and he finally concludes:
The error state is reset every time the hook runs again. That's useful
because after a failed request the user may want to try it again which
should reset the error.
actually I see that the useEffect to fetch data depends only on the url, that is the query string, hence, if the user doesn't change the query string, he can't try again. This can be useful especially to try after an error, but even more in general. How to achieve this goal?
I've tried
fetchData();
}, [url, isError]);
but it gets stuck in a loop of updates...
I'd add a boolean in state to track when to execute the fetchData()
const [loadContent, setLoadContent] = useState(true);
useEffect would look like this
useEffect(() => {
const fetchData = async () => {
setLoadContent(false) // Resetting the flag
setIsError(false);
setIsLoading(true);
try {
const result = await axios(`${url}${query}`);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
loadContent && fetchData() // Fetch data only if the flag is true
});
And finally form element like this
<form onSubmit={event => {
setLoadContent(true);
event.preventDefault();
}}>
Codesandbox link
My solution for this problem was
to add another hook state (representing the need to repeat the data fetch):
const [repeat, setRepeat] = useState(false);
to make the useEffect dependent on it (also adding a cleanup):
fetchData();
return function cleanup() {
setRepeat(false);
};
}, [url, repeat]);
and finally adding a check in the submit to force the data fetch even if the query string wasn't changed:
<form onSubmit={event => {
const before = url;
setUrl(algoliaUrl(query));
if (url === before)
{
setRepeat(true);
}
event.preventDefault(); }}>

Resources