React App component not rendering based on conditional logic - javascript

The logic of this component looks good to me.
Assuming isReceivedSession is true, a <ReactFragment/> renders; if not, a <Spinner/> should render
Within the fragment, if isLoadingApp || isLoadingAuth is truthy, a <Spinner/> component should render; if the statement is not truthy, a line and the router are returned and render.
But even when isLoadingApp logs true in the console, the spinner never shows up. I just see a fragment (nothing) or the expected line and router navigation path.
When isLoadingApp evaluates to true, a spinner component should be visible.
I feel like I'm missing something deeper here...where is the flaw in the logic?
const App = ({ classes }: IProps) => {
const dispatch = useDispatch();
const [isReceivedSession, setIsReceivedSession] = useState(false);
const isLoadingAuth: boolean = useSelector(authIsLoadingSelector);
const isLoadingApp: boolean = useSelector(appIsLoadingSelector);
useEffect(() => {
(async () => {
try {
const sessionData = await CognitoClient.currentSession();
const currentSessionToken = sessionData.getAccessToken().getJwtToken();
if (currentSessionToken) {
dispatch(authActions.setAuthStatus(AUTH_STATUS.LOGGED_IN));
}
} finally {
setIsReceivedSession(true);
}
})();
}, []);
//all logging values properly
console.log("isReceivedSession", isReceivedSession);
console.log("isLoadingApp ", isLoadingApp);
console.log("isLoadingAuth ", isLoadingAuth)
return isReceivedSession ? (
//spinner never renders even when isLoadingApp is true
<Fragment>
{isLoadingApp ||
(isLoadingAuth && <Spinner size={48} className={classes.spinner} />)}
<HeaderLine />
<Router>
<Switch>
<Route exact path={APP_AUTH_PATH()} component={SignInScreen} />
<PrivateRoute
path={APP_DASHBOARD_PATH()}
component={DashboardScreen}
authenticationPath={APP_AUTH_PATH()}
/>
<Route
exact
path={APP_LANDING_PATH()}
render={() => <Redirect to={APP_DASHBOARD_PATH()} />}
/>
</Switch>
</Router>
</Fragment>
) : (
<Spinner size={48} className={classes.spinner} />
);
};
export default withStyles(styles)(App);

The logical OR operator "||" doesn't look at the second condition whenever the first condition is already true.

Related

load routes after getting roles from an api

I created a react app, I added role based mechanism, the idea is that after the athentication, directly I send a request to an api to get the roles of the user, because the token contains only the username and I can not add any information on the payload.
so while getting thr roles from the api, I added a loder component that will block the user from using the app until the roles are loaded, at this point everything is working well, but when I reloaded the page, the app redirect me to the default route everytime, because the routes are not created yet, I would like to know how to block the app until the routes also are loaded? :
App.tsx :
const App: React.FC = () => {
const useRoles = useRoleBased(); // hook to get roles
return (
<>
{useRoles?.loading(
<Loader open backgroundColor="#ffffff" circleColor="primary" />
)}
<Box className={`mainSideBar ${openSideBar && 'openSideBar'}`}>
<Router />
</Box>
</>
);
};
export default App;
Router.tsx :
const routes = [
{ path: '/logout', element: <ConfirmLogout /> },
{
path: '/dashboard-page',
element: <DashboardPage />,
allowedRoles: [Roles.Director, Roles.RequestFullAccess],
},
{
path: '/profil',
element: <RequestPage />,
allowedRoles: [Roles.Manager],
},
];
const Router: React.FC = () => {
return <RolesAuthRoute routes={routes}></RolesAuthRoute>;
};
export default Router;
RolesAuthRoute.tsx :
export function RolesAuthRoute({ routes }: { routes: IRoutes[] }) {
const userRoles = useSelector((state: any) => state?.roles?.roles);
const isAllowed = (
allowedRoles: Roles[] | undefined,
userRoles: string[]) =>
process.env.REACT_APP_ACTIVATE_ROLE_BASED_AUTH === 'false' ||
!allowedRoles ||
allowedRoles?.some((allowedRole) => userRoles?.includes(allowedRole)
);
return (
<Routes>
{routes.map((route) => {
if (isAllowed(route?.allowedRoles, userRoles))
return (
<Route
path={route?.path}
element={route?.element}
key={route?.path}
/>
);
else return null;
})}
<Route path="*" element={<Navigate to="/" replace />} /> //this route is created in all cases
</Routes>
);
}
You could return early (conditional rendering) to stop the router from rendering prematurely. You'll need to modify the hook to return the loading state as boolean instead of rendering the component as it seems to be currently implemented.
const App: React.FC = () => {
const useRoles = useRoleBased(); // hook to get roles
if(useRoles.isLoading){
return <Loader open backgroundColor="#ffffff" circleColor="primary" />
};
return (
<>
<Box className={`mainSideBar ${openSideBar && 'openSideBar'}`}>
<Router />
</Box>
</>
);
};
export default App;

React context api lose auth data when react router dom push page

I have an context where i save the user data, and i have another component when verify the context user is null, if the context user is null my component should redirect the user to the login page, if not should render the component. My routers is inside my Authprovider, but still losing the user data when reload the router. I found another posts with the same issue, and the instruction is to keep the routers inside the useauthprovider, but doesn't work with my app.
My code
function App() {
let header = window.location.pathname === '/login' || '/cadastro' ? <Header /> : null;
let footer = window.location.pathname === '/login' || '/cadastro' ? <Footer /> : null;
return (
<UseAuthProvider> // My use AuthProvider
<Router>
<div className='app-container' >
<Switch>
<Cart>
<Header />
<NavbarMenu />
<div className='app-body'>
<UseCampanhaProvider>
<PublicRoute exact path='/' component={Home} />
<PrivateRoute exact path='/cupom/:campaignId' component={CupomScreen} />
<PrivateRoute exact path='/carrinho' component={CartScreen} />
</UseCampanhaProvider>
<PublicRoute exact path='/login' restricted={true} component={Login} />
<PublicRoute path='/cadastro' restricted={true} component={Cadastro} />
</div>
<AuthModal />
{footer}
</Cart>
</Switch>
</div>
</Router >
</UseAuthProvider>
);
}
export default App;
My component where i verify the user context
const PrivateRoute = ({ component: Component, ...rest }) => {
const { user } = useAuth();
return (
<Route {...rest} render={props => (
!user ?
<Redirect to='/login' />
:
<Component {...props} />
)} />
);
};
export default PrivateRoute;
My context where i load the user
const UseAuthProvider = ({ children }) => {
const [user, setUser] = useState();
const [open, setOpen] = useState(false)
useEffect(() => {
verifyUser(); //here i call the function when verify the localstorage
}, [])
const verifyUser = async () => {
let tokenHeader = authHeader();
if (tokenHeader) {
await Api.post('/cliente/index', {}, {
headers: {
...tokenHeader
}
}).then((response) => {
setUser(response.data.cliente)
})
}
}
const handleModal = () => {
setOpen((state) => !state)
}
const Logout = async () => {
localStorage.clear('acessToken-bolao')
setUser(null)
}
return (
<useAuthContext.Provider value={{ Auth, verifyUser, user, Register, Logout, open, handleModal }}>
{children}
</useAuthContext.Provider>
)
}
I tried to debug my application and when i redirect my user to another router, before the component render my user return undefined, and after my component is rendered the context load the user data.
It sounds like your entire application is unmounting and remounting.
In this case the state will be lost as it is not simply a re-render.
By what mechanism are you navigating to the new page?
If I remember React-Router correctly you need to use
If you try navigating the url itself with window.location or href then you are reloading the entire page (not using the router in the SPA)
If routed correctly I would expect that only data inside the Switch would be re-loaded.

ReactRouterDom, AuthRoute returns react render functions are not valid as react child warning

My Router is a simple component containing public and private routes. I have created an AuthRoute referring to the great tutorial from here
So, my Router looks like:
<Router>
<div>
<Navigation />
<Route exact path={ROUTES.LANDING} component={Landing} />
<Route path={ROUTES.SIGN_UP} component={SignUp} />
<Route path={ROUTES.SIGN_UP_SUCCESS} component={SignUpSuccess} />
<AuthenticationRoute path={ROUTES.HOME} component={Home} />
</div>
</Router>
and my AuthenticationRoute looks like this:
export const AuthenticationRoute = ({ component: Component, ...rest }) => {
const [authChecking, setAuthChecking] = useState(true);
const [{ isAuth }, dispatch] = useStateValue();
useEffect(() => {
checkLoggedIn().then(res => {
setAuthChecking(false);
dispatch({
op: 'auth',
type: 'toggleSessionAuth',
toggleSessionAuth: res
});
});
}, [])
if(authChecking)
return null;
if(!isAuth) {
return <Redirect to='/' />;
}
return <Route {...rest} render={(props) => (
<Component {...props} />
)
} />
}
Everything looks fine, however, my console returns such warning:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from the render. Or maybe you meant to call this function rather than return it.
I have tried different solutions using component/render etc, however, I could not find a solution to this problem and I have no idea what I am doing wrong.
For testing purposes, instead of rendering Component, I tried to render simple <div>test</div> and it worked fine. However, when I am passing a JSX component in props, it returns the warning shown above.
Implementation oh Home Component (Home.js):
export const Home = () => {
const [{ user }, dispatch] = useStateValue();
const { history } = useReactRouter();
const moveTo = path => {
dispatch({
op: 'other',
type: 'setView',
setView: path
});
history.push(path);
}
return (
<div className="pageMenuWrapper">
<h1 className="homeTitle">Hi {() => user ? `, ${user.username}` : ``}.</h1>
<div className="wrapper">
<Tile image={leagueico} alt="text" onClick={() => moveTo(ROUTES.TEST)}/>
<Tile comingSoon />
</div>
</div>
);
}
export default Home;
Could anyone help me solve this little problem?

React - useContext returns undefined

i'm trying to use React Context to manage authentication, but i can't see the value that return the context in PrivateRoute.js
App.js
render() {
return (
<>
<BrowserRouter>
<Islogin>
<Header/>
<Banner/>
<Switch>
<PrivateRoute exact path="/index" component={Landing} />
<PrivateRoute path="/upload" component={Upload} exact />
<PublicRoute restricted={false} path="/unauth" component={Unauthorized} exact />
</Switch>
</Islogin>
</BrowserRouter>
</>
);
}
}
export default App;
the console log of isAuthenticated returns undefined
PrivateRoute.js
const PrivateRoute = ({component: Component, ...rest}) => {
const isAuthenticated = useContext(AuthContext)
console.log(isAuthenticated)
const [validCredentials, setValidCredentials] = React.useState(false)
React.useEffect(() => {
if (typeof isAuthenticated === 'boolean') {
setValidCredentials(isAuthenticated)
}
}, [isAuthenticated])
return (
// Show the component only when the user is logged in
// Otherwise, redirect the user to /signin page
<Route {...rest} render={props => (
validCredentials ?
<Component {...props} />
: <Redirect to="/unauth" />
)} />
);
};
export default PrivateRoute;
IsLogin.js
The api call works and the console log shows true.
export default function Islogin({ children }) {
var [auth, setAuth] = React.useState(false)
React.useEffect(() =>{
axios.post('/api/auth').then(response => {
var res = response.data.result;
console.log("try")
console.log(res)
setAuth(res)
})
},[])
return (
<AuthContext.Provider value={auth}>
{children}
</AuthContext.Provider>
)
}
You may need to import it at the top of the file that you are using it in (PrivateRoute.js)
Try this:
import {useContext} from 'react'

React hooks - state in useState() is not reset when route is changed

const Create = () => {
console.log('rerender !!')
const [parcelType, setParcelType] = useState('paper')
console.log('parcelType =', parcelType)
return (
<Container onClick={() => setParcelType('plastic')}>
<BookingList />
<Card title="Business">
<p>Header</p>
</Card>
</Container>
)
}
export default Create
I want to change parcelType state to 'plastic' when click on Container in Create component. and I want to reset parcelType state to 'paper' when route is change ( Create component re-render ). But when component re-render state is not set to paper
For more details: CreateComponent is re-render when route is change in BookingList component
const BookingList = props => {
const { id } = props.match.params
const containerStyle = useTranslateSpring('-100px', '0')
const itemList = items.map((item, idx) => {
const itemStyle = useTranslateSpring('-100px', '0', '0', 200 + 200 * idx)
const url = `/booking/${item.id}/create`
return (
<ItemContainer
onClick={() => props.history.push(url)}
style={itemStyle}
key={item.id}
isactive={id === item.id}
>
{item.id}
</ItemContainer>
)
})
return <Container style={containerStyle}>{itemList}</Container>
}
export default withRouter(BookingList)
Create Component is render in route by routeTemplate
const Routes = () => (
<Router basename={process.env.REACT_APP_BASE_URL}>
<>
<RouteTemplate
exact
path="/booking/:id/create"
component={Booking.create}
title="Booking"
/>
</>
</Router>
)
and RouteTemplate is render Component wrapped by PageTemplate component
const RouteTemplate = props => {
const {
component: Component,
title,
query,
isAuthenticated,
isLanding,
...rest
} = props
return (
<Route
{...rest}
render={matchProps =>
isAuthenticated ? (
<PageTemplate title={title} isLanding={isLanding}>
<Component {...matchProps} query={query} />
</PageTemplate>
) : (
<Redirect
to={{
pathname: '/',
state: { from: props.location },
}}
/>
)
}
/>
)
}
So I assume you want to reset component's state once route is changed.
This should happen wherever you use functional component + hooks or class-based component with explicit this.state. It's how React works under the hood.
You already have <Create> rendered at the page
Once route is changed <Route> tries to render <Create> element
React sees there is already existing <Create> element and tries to update that instead of re-creating(typically update is much more efficient than re-creating). That's why state is not reset - since it should not reset for updates.
There are different way to handle that.
If such a case happen outside react-router's <Route> I'd suggest use key prop to reset state. But for <Route> it would mean replacing more clear/straightforward <Route path="..." component={Create} /> with more verboose <Route path="..." render={({match}) => <Create match={match} key={match.params.id} />}
So instead let's apply useEffect hook to reset state once props.match.params.id is changed:
const Create = ({ match: {params: {id} } }) => {
useEffect(() => {
setParcelType('paper');
}, [id]);
That should be equal to class-based
state = {
typeOfWhatEver: 'paper'
};
componentDidUpdate(prevProps) {
if(prevProps.match.params.id !== this.props.match.params.id) {
this.setState({
typeOfWhatEver: 'paper'
});
}
}

Categories

Resources