iterating on react components - javascript

i wanted to iterate on the list of components as you can there is lot of redundancy so for example i have long list of routes just as given below
<Route
exact
path={Routes.HOME}
render={() => (
<LandingPage
setValue={setValue}
setSelectedIndex={setSelectedIndex}
/>
)}
/>
<Route
exact
path={Routes.SERVICES}
render={() => (
<ServicesPage
setValue={setValue}
setSelectedIndex={setSelectedIndex}
/>
)}
/>
<Route
exact
path={Routes.MOBILE_APPS}
render={() => (
<MobileAppsPage
setValue={setValue}
setSelectedIndex={setSelectedIndex}
/>
)}
/>
i would create a list like below
const list = [
{path: Routes.HOME, component: LandingPage},
{path: Routes.SERVICES, component: ServicesPage},
{path: Routes.MOBILE_APPS, component: MobileAppsPage}];
and i want to achieve below
list.map((obj) => (
<Route
exact
path={obj.path}
render={() => (
<obj.component
setValue={setValue}
setSelectedIndex={setSelectedIndex}
/>
)}
/>
))

When rendering a custom component, you must have its name Capitalized:
// Good
<Component/>
<Obj.Component/>
<Obj.component/>
// Bad
<component/>
<obj.component/>
<obj.Component/>
list.map(({ path, component: Component }) => (
<Route
exact
path={path}
render={() => (
<Component setValue={setValue} setSelectedIndex={setSelectedIndex} />
)}
/>
));
// Same
list.map((obj) => {
const { component: Component, path } = obj;
return <Route path={path} render={() => <Component {...}/>} />;
});

Related

React Router V6 nested Routes with i18n

I have a question about React Router V6 nested with i18n.
This is my first multi-language service.
const MainPage:React.FC = () => {
const lang = i18n.language;
return (
<>
<Wrapper>
<Routes>
{/* Main */}
<Route path={`/`} element={<Home />}>
<Route path={`${lang}`}>
<Route path={`service`}>
<Route path={'slack'} element={<Slack />} />
</Route>
</Route>
{/* <Route path={`service/dooray`}element={<Dooray />} /> */}
{/* <Route path={`contact`} element={<Contact />} /> */}
{/* <Route path={`app/sign-in`} element={<SignIn />} /> */}
{/* <Route path={`app/sign-up`} element={<SignUp />} /> */}
{/* <Route path={`app/mail-code`} element={<MailCode />} /> */}
{/* <Route path={`app/password/reset`} element={<PwdReset />} /> */}
{/* <Route path={`policies/privac`} element={<Privacy />} /> */}
{/* <Route path={`policies/terms`} element={<Terms />} /> */}
</Route>
{/* <Route path={`*`} element={<>NOT FOUND</>} /> */}
{/* test */}
</Routes>
</Wrapper>
<ParentModal />
</>
If I enter localhost:3000/en, there is an error 'This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.'
How can I fix it..
I want /en => go to english page, /jp => go to japanese page
const MainPage:React.FC =() => {
...
<Route path={`/`} element={<Home />}>
<Route path={`/${lang}/*`}>
<Route path={`service`}>
<Route path="slack" element={<Slack />} />
</Route>
</Route>
</Route>
}
const Home:React.FC = () => {
return (
<>
... UI, JSX
<Outlet />
</>
)
}
I add a <Outlet />. But if I entered '/ko/service/slack', render <Home /> now
<Route path={`/`} element={<Home />}>
<Route path="service">
<Route path="slack" element={<Slack />} />
<Route path="dooray" element={<Dooray />} />
</Route>
</Route>
nested-routes doesn't work.. :(
I had the exact same useCase (localize react router v6) and came up with the following LangRouter repository link:
const LangRouter = () => {
const { i18n } = useTranslation(),
{ pathname, search, hash } = useLocation(),
navigate = useNavigate(),
availableLocales = ['en', 'ar'],
defaultLocale = (
getDefaultLanguage() === 'en' || getDefaultLanguage() === 'ar' ? getDefaultLanguage() : 'en'
) as string,
pathnameLocale = pathname.substring(1, 3).toLowerCase(),
[locale, setLocale] = useState(defaultLocale),
loaderTimerRef = useRef<any>(),
[isLoading, setIsLoading] = useState(true);
useEffect(() => {
loaderTimerRef.current = setTimeout(() => {
setIsLoading(false);
clearTimeout(loaderTimerRef.current);
}, 300);
}, []);
useEffect(() => {
if (availableLocales.includes(pathnameLocale)) {
updateLocale(pathnameLocale);
} else if (pathname === '/') {
updateLocale(defaultLocale);
}
// eslint-disable-next-line
}, [pathname]);
useEffect(() => {
let lang = defaultLocale;
if (availableLocales.includes(pathnameLocale)) {
lang = pathnameLocale;
setLanguageHandler(lang);
} else if (pathname === '/') {
setLanguageHandler(lang);
}
// eslint-disable-next-line
}, [locale]);
const setLanguageHandler = (lang: string) => {
if (lang === 'en') {
i18n.changeLanguage('en-US');
} else {
i18n.changeLanguage('ar-SA');
}
};
const updateLocale = (newLocale: string) => {
const newPath = `/${newLocale}` + pathname.substring(3);
if (locale !== newLocale) {
if (newPath === `/${newLocale}/` || newPath === `/${newLocale}` || pathname === '/') {
navigate(getHomePageUrl(newLocale));
} else {
navigate(`${newPath}${hash}${search}`);
}
setLocale(newLocale);
} else if (newPath === `/${newLocale}/` || newPath === `/${newLocale}` || pathname === '/') {
if (isAuthenticated()) {
navigate(getHomePageUrl(newLocale));
} else {
navigate(getLoginPageUrl(newLocale));
}
}
};
if (isLoading) {
return (
<div className="loader-wrapper">
<LoadingIcon />
</div>
);
}
return (
<LocaleContext.Provider value={{ locale, setLocale: updateLocale }}>
<Routes>
<Route path={`/${locale}`} element={<App />}>
{publicRoutes.map((el, i) => (
<Route
key={i}
path={el.path(locale)}
element={
<PublicRouteGuard
restricted={el.restricted}
redirect={el.redirect ? el.redirect(locale) : undefined}
>
{el.element}
</PublicRouteGuard>
}
/>
))}
{privateRoutes.map((el, i) => (
<Route
key={i}
path={el.path(locale)}
element={
el.permissions ? (
<RestrictedRouteGuard requiredPermissions={el.permissions}>
{el.element}
</RestrictedRouteGuard>
) : (
<PrivateRouteGuard>{el.element}</PrivateRouteGuard>
)
}
>
{el.children &&
el.children.map((innerEl, innerI) => (
<Route key={innerI} path={innerEl.path(locale)} element={innerEl.element} />
))}
</Route>
))}
</Route>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</LocaleContext.Provider>
);
};
export default LangRouter;
Issue
The error 'This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.' means the parent route isn't rendering an Outlet component for the nested routes to be rendered into. The route rendering the Home component doesn't appear to be rendering an Outlet.
Solution
Update the Home component to render an Outlet. Note that Route components without an element prop will render an Outlet by default.
Example:
import { Outlet } from 'react-router-dom';
const Home = () => {
...
return (
<>
... home page UI/JSX ...
<Outlet />
</>
);
};
...
const MainPage:React.FC = () => {
const lang = i18n.language;
return (
<>
<Wrapper>
<Routes>
{/* Main */}
<Route path="/" element={<Home />}>
<Route path={lang}> // <-- renders Outlet by default
<Route path="service"> // <-- renders Outlet by default
<Route path="slack" element={<Slack />} />
</Route>
</Route>
...
</Route>
...
{/* test */}
</Routes>
</Wrapper>
<ParentModal />
</>
);
};
Update
If the Home and Slack components are separate and independent, then move the Home component into an index route and simplify the routing to the Slack component.
<Routes>
<Route path="/">
<Route index element={<Home />} />
<Route path={`${lang}/service/slack`} element={<Slack />} />
</Route>
</Routes>

How to use array of routes?

I'm newbie with react but i saw something weird. I've got all of my routes something like below:
<Main>
<Route exact path="/home" component={Home} />
<Route exact path="/home1" component={Home1} />
<Route exact path="/home2" component={Home2} />
<Route exact path="/home3" component={Home3} />
<Route exact path="/home4" component={Home4} />
<Route exact path="/home5" component={Home5} />
<Redirect from="*" to="/home" />
</Main>
is there any way to use an array and map instaed of paste another one <Route /> component?
I think about somehting like this:
routes.ts
const routes = [
{ path: "/home", component: SomeComponent },
{ path: "/home2", component: SomeComponent2 },
];
indes.tsx
<Main>
{ routes.map(route => (
<Route exact path={route.path} component={route.component} />
))}
</Main>
but i don't know how to pass new component into object in routes list.
Thanks for any help!
Note: Which react-router-dom version are u using? This is a code example for RRD6
const routes = [
{ name: "/route1", component: <ClickLabel /> },
{ name: "/route2", component: <Focus /> },
{ name: "/route3", component: <Counter /> }
];
/*--------------***/
{routes.map(({ name, component }, i) => (
<Route key={i} path={name} element={component} />
))}
Here is example:
https://codesandbox.io/s/happy-swartz-ikqdn?file=/src/App.js
If u are using RRDv5, then here is the code
{/* Want to pass some dynamic props */}
{routes.map(({ name, component }, i) => (
<Route
key={i}
path={name}
render={() => {
return React.createElement(
component,
{ p: "THis is the prop" },
null
);
}}
/>
))}
{/* Simply render a component */}
{routes.map(({ name, component }, i) => (
<Route key={i} path={name} component={component} />
))}

React router not working on live production

I have built a UI amazon-clone with create-react-app
it only shows dummy data.
the problem is after publishing it to Vercel, the routing not working as expected! after clicking the links you see a blank page "URL params are correct", you have to manually reload the page to work!
also if you clicked a button no event trigger and you get a blank page!
I wrapped all my routes to MainRoute Component:
const MainRoute = withRouter(({ location }) => {
return (
<>
{location.pathname !== "/login" && location.pathname !== "/signup" ? (
<Header />
) : null}
<Switch>
<Route exact path="/" render={() => <Home />} />
<Route exact path="/products" render={() => <Products />} />
<Route
exact
path="/products/:productID"
render={() => <ProductPage />}
/>
<Route path="/checkout" render={() => <Checkout />} />
<Route path="/payment" render={() => <Payment />} />
<Route path="/login" render={() => <Login />} />
<Route path="/signup" render={() => <Signup />} />
<Route render={() => <NotFoundPage />} />
</Switch>
{location.pathname !== "/login" && location.pathname !== "/signup" ? (
<Footer />
) : null}
</>
);
});
export default withRouter(MainRoute);
my App Component:
function App() {
return (
<div className="app_wrapper">
<Router>
<MainRoute />
</Router>
</div>
);
}
export default App;
repo
https://github.com/aseelban/amazon-clone-app
link:
https://amazon-clone-app-llyl1tfcn.vercel.app/
it works correctly (under Brave browser) the authentication routes, could you please specify which route the issue occurs.!
Thanks, everyone for the help.
I solved this problem by removing HOC withStyles and instead use react-jss.

#reach/router nested protected route

I use reach/router with custom protected route like this :
const ProtectedRoute = ({ component: Component, ...rest }) => (
localStorage.getItem('user_id') ? <Component {...rest} /> : <Redirect from="" to="/login" noThrow />
);
const LoginRoute = ({ component: Component, ...rest }) => (
localStorage.getItem('user_id') ? <Redirect from="" to="/home" noThrow /> : <Component {...rest} />
);
const PublicRoute = ({ component: Component, ...rest }) => (
<Component {...rest} />
);
<Router>
<LoginRoute component={Login} path="/login" />
<PublicRoute default component={Notfound} />
<ProtectedRoute component={landingPage} path="/home" />
<ProtectedRoute path="/" component={App} />
<ProtectedRoute component={UserList} path="user" />
<ProtectedRoute component={UserDetail} path="user/create" />
</Router>
i want this to be nested route with user/:id
<ProtectedRoute component={UserList} path="user" />
<ProtectedRoute component={UserDetail} path="user/create" />
what should i do?
I Feel like you're complicating things
const Routes = () => {
const [loggedIn, setLoggedIn] = useState(false)
useEffect(() => {
localStorage.getItem('user_id') && setLoggedIn(true)
}, [])
return (
<Router>
<LoginRoute component={Login} path="/login" />
<Notfound default />
{
loggedIn
? (
<>
<LandingPage path="/home" />
<App path="/" component={App} />
<UserList component={UserList} path="user">
<UserDetail component={UserDetail} path="create" />
</UserList>
</>
) : (
<Redirect from="" to="/login" noThrow />
)
}
</Router>
)
}
In case this didn't work as intended or you feel you want to use it in your way, do this
<Router>
...
<ProtectedRoute component={UserList} path="user">
<UserDetail path="create" />
</ProtectedRoute>
</Router>
No need of using ProtectedRoute HOC for UserDetail since it's already nested under ProtectedRoute
and in UserList use props.children to render UserDetail

If condition to change route using React Router V4

I have two types of routes, countries and content:
/countries
/industry/ranking
/industry/audience
/website/ranking
/website/audience
/website/conversion
etc...
When the user enters the application, I have to verify if he already chose a country, which I'm saving using localStorage.
If the user has the country already chosen, he needs to go to /industry/ranking, if don't, to /countries.
I'm receiving a warning about the route changing by code:
<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.
My code:
<Switch>
<Route exact path='/countries' component={Countries} />
{
current && (
<React.Fragment>
<Route exact path='/industry/ranking' render={() => <IndustryRanking country={current} />} />
<Route path='/industry/audience' render={() => <IndustryAudience country={current} />} />
<Route path='/website/ranking' render={() => <WebsiteRanking country={current} />} />
<Route path='/website/audience' render={() => <WebsiteAudience country={current} />} />
<Route path='/website/device' render={() => <WebsiteDevice country={current} />} />
<Route path='/website/conversion-orders' render={() => <WebsiteConversionOrders country={current} />} />
</React.Fragment>
)
}
{ !current ? <Redirect to='/countries' /> : <Redirect to='/industry/ranking' /> }
</Switch>
Is there a way to improve this using just the routes to verify my condition?
Thanks!
You can simply write a custom component that renders the Routes or redirects to the country instead of conditionally rendering the Routes which is causing this warning
const CustomRoute = (props) => {
const current = localStorage.getItem('country');
if(current) {
return <Route {...props} />
}
return <Redirect to='/countries' />
}
and use it like
<Switch>
<CustomRoute exact path='/countries' component={Countries} />
<CustomRoute exact path='/industry/ranking' render={() => <IndustryRanking country={current} />} />
<CustomRoute path='/industry/audience' render={() => <IndustryAudience country={current} />} />
<CustomRoute path='/website/ranking' render={() => <WebsiteRanking country={current} />} />
<CustomRoute path='/website/audience' render={() => <WebsiteAudience country={current} />} />
<CustomRoute path='/website/device' render={() => <WebsiteDevice country={current} />} />
<CustomRoute path='/website/conversion-orders' render={() => <WebsiteConversionOrders country={current} />} />
<Redirect to='/industry/ranking' />
</Switch>

Categories

Resources