Don't update route component when redux state changes - reactjs

How to don't update component rendered by Route if something in redux changed? Added some examples of routing, component which lazy imported has redux store connection.
const ROUTES_OPTIONS = [
{
path: `${PATH_TO_SMART_ACCESS}${PATH_TO_SMART_ACCESS_VIEW}/options`,
Component: lazy(() => import("./SmartAccessActionsOptions")),
exact: false
},
{
path: `${PATH_TO_SMART_ACCESS}${PATH_TO_SMART_ACCESS_VIEW}/:id/options/:type?`,
Component: lazy(() => import("./SmartAccessActionsOptions")),
exact: false
}
];
const Routes = () => {
return (
<Switch>
{ROUTES_OPTIONS.map(route => (
<Route
key={route.path}
exact={route.exact}
render={() => {
const Component = route.Component;
return <Component />;
}}
/>
))}
</Switch>
);
};

Related

How can I use an array map of routes with BrowserRouter and Switch without them all loading at once?

Routes:
const issueSubmissionModal = () => IssueModal({ isVisible: true })
const renderHeader = () => <AppHeader userData={userData}><AppNavbar/></AppHeader>
const renderHome = () => <AppTemplate userData={userData} />
const Routes = userData ? [
{
key: "home",
path: "/",
component: renderHome(),
includeProfile: null,
includeAppheader: renderHeader()
},
{
key: "issueSubmission",
path: "/issueSubmission",
component: issueSubmissionModal(),
includeProfile: renderHome(),
includeAppheader: renderHeader()
},
] : []
const renderRoutes = () => <Switch>
{Routes.map(route => (
<Route exact path={route.path} key={route.key}>
{route.includeProfile && route.includeProfile}
{route.component}
</Route>
))}
</Switch>
return renderRoutes();
App.tsx:
function App() {
const { data: userData, isLoading, isError, error } = useAppUser();
return (
<div className="App">
<Router>
{userData ?
<Routes userData={userData} />
: null}
</Router>
</div>
);
}
export default App;
Problem? No matter what I do, all routes render as soon as the page launches. I simplified to two routes here, but the actual app has several.
It seems to me Switch isn't working. I don't know why.
I can go back to just having all the routes written out manually in App.tsx, but that's gross and inefficient for what we're trying to do.
Any React gurus got an idea?
Probably you are missing to include component tag within <Route />
{
Routes.map(route => (
<Route exact path={route.path} key={route.key} component=
{route.includeProfile ? route.component} />
)
)
}

React hooks router is not rendering component

Im trying to lazy load my components with react-router. But component is not rendering after redirect. I have to refresh page manually to see the component. Where is my fault ?
routes.js ;
import React from "react";
const Bank = React.lazy(() => import("containers/HomePage"));
const MoneyWithdraw = React.lazy(() => import("containers/Withdraw"));
const routes = [
{ path: "/main", exact: true, component: Bank },
{ path: "/withdraw", exact: true, component: MoneyWithdraw },
];
export default routes;
index.js ;
<div
hidden={process.env.NODE_ENV === 'development' ? !hidden : hidden}
className={hidden ? 'app-show' : 'app-hide'}>
<Suspense fallback={<div>test</div>}>
<Router history={history}>
<MenuSideBar />
<Switch>
{routes.map((route, idx) => {
return route.component ? (
<Route
key={idx}
path={route.path}
exact={route.exact}
render={props => <route.component {...props} />}
/>
) : null;
})}
</Switch>
</Router>
</Suspense>
</div>
I used history as ;
import { createHashHistory } from 'history';
export default createHashHistory();

How can I get a component to render as a parent of protected routes in react router dom?

Here is the code I am working with:
App.js
import NotFound from "components/NotFound";
import Loading from "components/Loading";
import PrivateRoute from "components/PrivateRoute";
import { logoutUser, setCurrentUser } from "features/auth/auth.slice";
import jwt_decode from "jwt-decode";
import React, { lazy, Suspense, useEffect } from "react";
import { useDispatch } from "react-redux";
import { Route, Switch, Redirect, useHistory } from "react-router-dom";
import HomeNav from "components/HomeNav";
// auth
const Login = lazy(() => import("features/auth/Login"));
const Register = lazy(() => import("features/auth/Register"));
const ForgotPassword = lazy(() => import("features/auth/ForgotPassword"));
const ResetPassword = lazy(() => import("features/auth/ResetPassword"));
// recipe
const Home = lazy(() => import("features/recipes/Home"));
const MyLikes = lazy(() => import("features/recipes/MyLikes"));
const MyRecipes = lazy(() => import("features/recipes/MyRecipes"));
const CreateRecipe = lazy(() => import("features/recipes/CreateRecipe"));
const EditRecipe = lazy(() => import("features/recipes/EditRecipe"));
const RecipeDetail = lazy(() => import("features/recipes/RecipeDetail"));
const UserProfile = lazy(() => import("features/recipes/UserProfile"));
const TopRecipes = lazy(() => import("features/recipes/TopRecipes"));
// user
const MyProfile = lazy(() => import("features/user/MyProfile"));
const App = () => {
const history = useHistory();
const dispatch = useDispatch();
// Check if user is already logged in
useEffect(() => {
const token = localStorage.token;
// Check for token to keep user logged in
if (token) {
try {
// Decode token and get user info and exp
const { exp, ...rest } = jwt_decode(token);
// Set user and isAuthenticated
dispatch(setCurrentUser(rest));
// Check for expired token
const currentTime = Date.now() / 1000; // to get in milliseconds
if (exp < currentTime) {
// Logout user
dispatch(logoutUser());
// Redirect to login
history.push("/");
}
} catch {
// Logout user
dispatch(logoutUser());
// Redirect to login
history.push("/");
}
}
}, []);
const authRoutes = [
{
path: "/",
Component: Login,
},
{
path: "/register",
Component: Register,
},
{
path: "/forgotpassword",
Component: ForgotPassword,
},
{
path: "/resetpassword/:resetToken",
Component: ResetPassword,
},
];
const privateRoutes = [
{
path: "/home",
Component: Home,
},
{
path: "/top-3",
Component: TopRecipes,
},
{
path: "/my-likes",
Component: MyLikes,
},
{
path: "/my-recipes",
Component: MyRecipes,
},
{
path: "/profile",
Component: MyProfile,
},
{
path: "/create",
Component: CreateRecipe,
},
{
path: "/recipe/:recipeId",
Component: RecipeDetail,
},
{
path: "/update/:recipeId",
Component: EditRecipe,
},
{
path: "/profile/:username",
Component: UserProfile,
},
];
return (
<Suspense fallback={<Loading />}>
<Switch>
{/* auth routes */}
{authRoutes.map(({ path, Component }, i) => (
<Route exact path={path} component={Component} key={i} />
))}
{/* private routes */}
{privateRoutes.map(({ path, Component }, i) => (
<PrivateRoute exact path={path} Component={Component} key={i} />
))}
{/* not found */}
<Route path="/404" component={NotFound} />
<Redirect to="/404" />
</Switch>
</Suspense>
);
};
export default App;
PrivateRoute.js
import React from "react";
import { Route, Redirect, useLocation } from "react-router-dom";
import { useSelector } from "react-redux";
import HomeNav from "components/HomeNav";
const PrivateRoute = ({ Component, ...rest }) => {
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
const { pathname } = useLocation();
return (
<Route
{...rest}
render={(props) =>
isAuthenticated ? (
<HomeNav>
<Component {...props} />
</HomeNav>
) : (
<Redirect to={{ pathname: "/", from: pathname }} />
)
}
/>
);
};
export default PrivateRoute;
This is the result I was looking for in that it renders the HomeNav component as a parent on all protected routes, but it causes a rerender each time a new link is visited. In addition, if I just wrap the PrivateRoutes inside of App.js in a HomeNav, it also works, but does not properly show the Not Found page. Any help would be appreciated!
You can render a single private Route component that specifies an array of private paths and render the HomeNav as a wrapper here and then render another Switch with the actual private paths.
App
<Switch>
{/* auth routes */}
{authRoutes.map(({ path, Component }, i) => (
<Route exact path={path} component={Component} key={i} />
))}
{/* private routes */}
<PrivateRoute path={privateRoutes.map(({ path }) => path)}>
<HomeNav>
<Switch>
{privateRoutes.map(({ path, Component }, i) => (
<PrivateRoute exact path={path} Component={Component} key={i} />
))}
</Switch>
</HomeNav>
</PrivateRoute>
{/* not found */}
<Route path="/404" component={NotFound} />
<Redirect to="/404" />
</Switch>
And of course you will need to remove HomeNav from PrivateRoute
const PrivateRoute = ({ Component, ...rest }) => {
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
const { pathname } = useLocation();
return (
<Route
{...rest}
render={(props) =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: "/", from: pathname }} />
)
}
/>
);
};

children routes, doesn't work React Router with Lazy Loading

Guys anyone would know how to help me how to use Child routes with Lazy loading with react-router.
I'm doing it the way below but when I try to access the sub route it doesn't work
-- the route is declared as a array
import { lazy } from 'react'
const routesLIst = [
{
path: '/myPage',
name: 'myPage',
Component: lazy(() => import('src/pages/myPage')),
children: [
{ path: '/test', name: 'test', Component: lazy(() => import('src/components/myPage/test')) },
{ path: '/test2', name: 'test2', Component: lazy(() => import('src/components/myPage/test2')) },
]
}
-in the page I'm trying to acces the data with props.children, but the sub-route doesn't work
{ props.children }
example: I'm trying to access myPage/test , and get the data inside the myPage with props.children
const myPage: FC<> = (props) => {
const { children } = props
return (
<div>
{ children }
</div>
)
}
import {Route, Switch} from 'react-router';
interface Routes {
routes?: any
}
export const Routes: FC<Routes> = (props) => {
const {routes} = props
return (
<>
<Switch>
{routes.map((route) => {
if (!route.children || route.children.length < 1) {
return (
<Route
key={route.path}
exact={route.exact}
path={route.path}
render={props => (<route.Component {...props} name={route.name}/>)}
/>
)
} else {
return (
<Route
key={route.path}
exact={route.exact}
path={route.path}
render={props => (
<route.Component {...props} name={route.name}>
{
route.children.map((subRoute) => (
<Route
key={subRoute.path}
exact={subRoute.exact}
path={subRoute.path}
render={props => (
<subRoute.Component {...props} name={subRoute.name}/>
)}
/>
))
}
</route.Component>
)
}
/>
)
}
}
)}
</Switch>
</>
);
};

How to configure react-route with private route?

I cannot configure the route correctly. Shop component not renders
Component structure:
index.tsx:
const Index: FC = () => {
const SignIn = props => (
<LazyLoad
component={React.lazy(() => import('./modules/auth/containers/SignIn'))}
{...props}
/>
)
return (
<Provider store={store}>
<EmotionThemeProvider>
<ThemeProvider theme={theme}>
<Router>
<Switch>
<PrivateRoute exact path='/' component={Main} />
<Route path='/signin' component={SignIn} />
</Switch>
</Router>
</ThemeProvider>
</EmotionThemeProvider>
</Provider>
)
}
PrivateRoute.tsx:
export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
localStorage.getItem('accessToken') ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: '/signin', state: { from: props.location } }}
/>
)
}
/>
)
Main.tsx:
const Main: FC = () => (
<App>
<Switch>
{routes.map(route => (
<Route
key={route.path}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</App>
)
Shop.tsx:
const Shop = () => {
const [, setCollapsed] = useContext(SidebarContext)
useEffect(() => {
setCollapsed(true)
}, [])
return <div>Shop</div>
}
routes.ts:
export const routes = [
{
path: '/shop',
key: 'market',
component: props => (
<LazyLoad
component={React.lazy(() => import('./modules/shop/pages/Shop'))}
{...props}
/>
),
},
{
path: '/',
exact: true,
component: () => <div>main</div>,
skip: true,
},
]
When I logged in, the main page opens, but after when I go to the shop, it no longer renders.
Where is my mistake? I have tried to remove 'exact' from PrivateRoute, then shop opens, but I got this error:
Uncaught Error: Maximum update depth exceeded. This can happen when a
component repeatedly calls setState inside componentWillUpdate or
componentDidUpdate. React limits the number of nested updates to
prevent infinite loops

Resources