最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React Router V6 nested Routes with i18n - Stack Overflow

programmeradmin1浏览0评论

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 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.. :(

Share Improve this question edited Apr 7, 2022 at 3:03 Jerry asked Apr 6, 2022 at 15:23 JerryJerry 411 silver badge6 bronze badges 1
  • now, I fix my code ``` <Route path={/} element={<Home />} /> <Route path={/${lang}} element={<Home><Outlet /></Home>}> <Route path={service} element={<Outlet />}> <Route path={'slack'} element={<Slack />} /> </Route> </Route>``` – Jerry Commented Apr 6, 2022 at 15:31
Add a ment  | 

3 Answers 3

Reset to default 4

You can create a Route with an Outlet that detects the language and assigns it to i18next directly:

<Routes>
  <Route path="/" element={<Home />}>
    <Route path=":lang" element={<LanguagePath />}>
      <Route path="/" element={<Home />} />
      <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="*" element={<>NOT FOUND</>} />
    </Route>
  </Route>
</Routes>

Then inside LanguagePath.js you can detect the lang and either set it in i18n.changeLanguage or redirect the user if it is missing.

export default function LanguagePath() {
  const { i18n } = useTranslation();
  const { lang } = useParams();
  const navigate = useNavigate();
  const curPath = location.pathname;
  useEffect(() => {
    if (lang && i18n.resolvedLanguage !== lang) {
      if (i18n.options.fallbackLng.includes(lang)) {
        i18n.changeLanguage(lang);
      } else {
        navigate("/" + i18n.resolvedLanguage + curPath, {replace: true});
      }
    }
  }, [lang]);
  return <Outlet />;
}

Now if a user arrives at:

/ => /
/contact => /en/contact
/en/contact => /en/contact
/fr/contact => /fr/contact

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 ponent for the nested routes to be rendered into. The route rendering the Home ponent doesn't appear to be rendering an Outlet.

Solution

Update the Home ponent to render an Outlet. Note that Route ponents 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 ponents are separate and independent, then move the Home ponent into an index route and simplify the routing to the Slack ponent.

<Routes>
  <Route path="/">
    <Route index element={<Home />} />
    <Route path={`${lang}/service/slack`} element={<Slack />} />
  </Route>
</Routes>

发布评论

评论列表(0)

  1. 暂无评论