Before rendering, my component should find out whether user authorized.
Function checkAuth( ) do this task.
While function does its work, state isLoading
equal true. Before the end of its work, the function assigns state isLoading
to false
While this state equal true, component renders loading animation.
When state equal false, component renders another content
My problem is that the component is looping to render over and over again. If I understand correctly, the state changes in the function checkAuth()
, and component as observer reacts to this changing and re-renders itself. After rerender, the function checkAuth()
starts again and so on in a circle.
I tried call this function inside useEffect and outside of this. Nothing helped me.
I expecting: function checkAuth() runs state isLodaing assigns to true component renders checkAuth continues to do its job before end of function checkAuth, state isLoading assigns to false
component observes this stop rendering start rendering another content: routes
Thank you very much for reading.
Component:
const AppRouter: FC = () => {
const controller = useContext(ControllerContext)
useEffect(() => {
controller.checkAuth()
})
if (controller.appStore.getIsLoading) {
console.log('loading');
return (<Loading />);
}
else return (
<div>routes</div>
// <Routes>
// {/* Private routes for authorized user */}
// <Route element={<PrivateRoutes />}>
// <Route element={<MainPageView />} path="/" />
// <Route element={<SettingsPage />} path="/settings" />
// </Route>
// {/* Public routes for unauthorized users */}
// <Route element={<UnauthorizedRoutes />}>
// <Route element={<LoginPage />} path="/login" />
// <Route element={<SignupPage />} path="/signup" />
// </Route>
// <Route element={<ErrorPage />} path="*" />
// </Routes>
)
};
export default observer(AppRouter);
Function checkAuth():
async checkAuth() {
this.appStore.setLoading(true)
try {
this.userStore.setUser({
nickname: 'igor',
email: 'igorasdasda',
id: 1,
avatar: '',
about: '',
isOnline: true,
onlineTime: ''
});
this.appStore.setAuth(true)
}
catch (e: any) {
console.log(e.response.data)
}
finally {
this.appStore.setLoading(false)
console.log(this.userStore.getUser);
}
}
My state stores:
export class UserStore {
constructor() { makeObservable(this) }
@observable private user = {} as IUser
@computed get getUser() {
return this.user
}
@action setUser(newUser: IUser) {
this.user = newUser
}
}
export class AppStore {
constructor() { makeObservable(this) }
// Loading state
@observable private isLoading = false
@computed get getIsLoading() {
return this.isLoading
}
@action setLoading(value: boolean) {
this.isLoading = value
}
// Auth state
@observable private isAuth = false;
@computed get getIsAuth() {
return this.isAuth
}
@action setAuth(value: boolean) {
this.isAuth = value
}
}
Your useEffect
should have the controller
as the only dependency. like this:
useEffect(() => {
controller.checkAuth();
}, [controller]);
With no dependency array (Like you did), the useEffect
will trigger every render, which will cause another checkAuth
, and you don't want that. Having the controller
as your only dependency means that only if the reference of the controller changes (Which will not happen) the useEffect
will run again.
Few more comments about your code:
In your stores, you don't have to create a private
observable, a getter and a setter, like you did with isLoading
. it's redundant. you just make the the field public
without the setter and the getter.
I would avoid export default
It's better to put the observer
on the function itself, while naming it (Better for the react dev tools), like this:
const AppRouter: FC = observer(function AppRouter() {