React Render UI Before Redirect - reactjs

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>
);

Related

Previous data showing even though cleaning up in useEffect

I have a component in my react native app that loads sessions related to a particular individual. In the useEffect() of that component I both load the sessions when the component comes into focus, and unload those sessions within the cleanup.
export const ClientScreen = (props) => {
const isFocused = useIsFocused();
const client = useSelector((state) => selectActiveClient(state));
useEffect(() => {
if (isFocused) {
const loadSessions = async () => {
if (client?.id) {
dispatch(await loadClientSessions(client?.id));
}
return () => dispatch(unloadSessions()); // Cleaning up here...
};
loadSessions(props);
}
}, [isFocused, client?.id]);
const updatedProps = {
...props,
client,
};
return <ClientBottomTabNavigator {...updatedProps} />;
};
Generally the component is working as expected. However, I do notice that if I load the component with one client, then navigate away, and then come back to the component by loading a new client, that for a brief moment the sessions pertaining to the previous client show before being replaced the sessions relevant to the new client.
My question is, shouldn't the unloadVisits() that runs on cleanup -- which sets sessions to an empty array -- prevent this? Or is this some kind of react behavior that's holding onto the previous state of the component? How can I ensure this behavior doesn't occur?
Cleanup function should appear before the closing-brace of the useEffect hook
useEffect(() => {
if (isFocused) {
const loadSessions = async () => {
if (client?.id) {
dispatch(await loadClientSessions(client?.id));
}
};
loadSessions(props);
}
return () => dispatch(unloadSessions()); // Cleaning up here... // <--- here
}, [isFocused, client?.id]);
as commented, your loadSessions returns a cleanup function, but you don't do anything with it. And the effect where you call loadSessions(props) does not return anything, that's why it does not clean up.
Edit:
I made a mistake, loadSessions returns a Promise of a cleanup function. And it is impossible to "unwrap" this Promise and get to the cleanup function itself in a way that you can return it in your effect. You have to move the cleaup function out of the async function loadSessions.
But you don't need async/await for everything:
useEffect(() => {
if (isFocused && client?.id) {
loadClientSessions(client.id).then(dispatch);
return () => dispatch(unloadSessions());
}
}, [isFocused, client?.id]);

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.

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);
}
});

Function inside useEffect fire twice even with empty dependency array

I have this example from https://github.com/vercel/next.js/blob/canary/examples/with-firebase-authentication/utils/auth/useUser.js
The effect works fine (fires once) but for some reason, the functions inside are called twice.
useEffect(() => {
const cancelAuthListener = firebase
.auth()
.onIdTokenChanged(async (user) => {
console.log('once or twice?')
if (user) {
// This fires twice
const userData = await mapUserData(user)
setUserCookie(userData)
setUser(userData)
} else {
removeUserCookie()
setUser()
}
})
const userFromCookie = getUserFromCookie()
if (!userFromCookie) {
router.push('/')
return
}
setUser(userFromCookie)
console.log(' i fire once')
return () => {
console.log('clean up')
cancelAuthListener()
}
}, [])
How can I make it to fire once?
I added some console logs:
On the first render I get: 'i fire once', 'once or twice', 'once or twice'
If I leave the page the cleanup console log fires (as it's supposed to do)
Many thanks
Later edit:
this is the code
export const mapUserData = async (user) => {
const { uid, email } = user
const token = await user.getIdToken()
return {
id: uid,
email,
token
}
}
If getIdToken() gets 'true' as an argument it will force a refresh regardless of token expiration.
https://firebase.google.com/docs/reference/js/firebase.User#getidtoken
Solved!!
the user was calling getIdToken(true) which forces a refresh.
https://firebase.google.com/docs/reference/js/firebase.User#getidtoken
Sorry guys, my bad!!!
You have a setState() inside useEffect thats the culprit, where useEffect having empty params [], one request on initial mount and another when do
setUser(userData) the component re-renders and useEffect() is invoked again.
Instead of using user as state, try using as ref and check. That might resolve this.

React Component gets unmounted and i don't know why

I'm a completely new to the whole react world but I'm trying to develop a SPA with a integrated calendar. I'm using react-router for routing, react-big-calendar for the calendar, axios for my API calls and webpack.
Whenever I'm loading my Calender Component it gets mounted and unmounted several times and I think that causes my API call to never actually get any data. I just can't figure out what is causing this.
The Code:
useEffect(() => {
console.log("mounting Calendar")
let source = Axios.CancelToken.source()
if(!initialized) {
console.log("getting Data")
getCalendarEvents(source)
}
return () => {
console.log("unmounting Calendar")
source.cancel();
}
})
const getCalendarEvents = async source => {
setInitialized(true)
setLoading(true)
try {
const response = await getCalendar({cancelToken: source.token})
const evts = response.data.map(item => {
return {
...item,
}
})
calendarStore.setCalendarEvents(evts)
} catch (error) {
if(Axios.isCancel(error)){
console.log("caught cancel")
}else{
console.log(Object.keys(error), error.message)
}
}
setLoading(false)
}
This is the result when i render the component:
Console log
If you need any more code to assess the problem, I will post it.
I appreciate any kind of input to solve my problem.
Thank you
Its because of the useEffect. If you want it to run just once on mount you need to pass an empty array as a dependency like so :
useEffect(() => {
console.log("mounting Calendar")
let source = Axios.CancelToken.source()
if(!initialized) {
console.log("getting Data")
getCalendarEvents(source)
}
return () => {
console.log("unmounting Calendar")
source.cancel();
}
},[])
This means it will only run once. If there is some state or prop you would like to keep a watch on you could pass that in the array. What this means is that useEffect will watch for changes for whatever is passed in its dependency array and rerun if it detects a change. If its empty it will just run on mount.

Resources