برنامه Symfony – React Spa. مشکل بارگیری مجدد

مقدمه
در این مقاله ، من می خواهم مشکلی را که در هنگام توسعه آبگرم در یک پروژه Symfony و نحوه حل آن با شما روبرو شدم با شما به اشتراک بگذارم.
زمینه
من در حال کار بر روی یک برنامه مبتنی بر Symfony هستم که از Symfony UX برای ادغام یک جبهه React-Spa در برنامه Symfony استفاده می کند. Frontend React با استفاده از یک کنترلر Symfony بارگذاری می شود که یک فایل شاخه ای را ارائه می دهد که مؤلفه اصلی React را ارائه می دهد:
#[Route('/app', name: 'get_app', methods: ['GET'])]
public function getApp(): Response
{
return $this->render('App.html.twig');
}
پرونده Twig به سادگی مؤلفه اصلی React را که بارش Spa React را بار می کند ، ارائه می دهد.
charset="utf-8" />
http-equiv="X-UA-Compatible" content="IE=edge" />
name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
name="description" content="" />
name="author" content="" />
{% block title %}Welcome!{% endblock %}
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
{{ react_component('App') }} >
AppComponent اصلی React به این شکل است:
export default function App() {
const wallet: Wallet = useStellarWallet(WalletNetwork.TESTNET);
return (
<Fragment>
<CssBaseline />
<WalletContext.Provider value={wallet}>
<Router>
<Routes>
<Route path="/login" element={<SignIn />} />
<Route path="/app" element={<ProtectedRoute children={<Layout /> } /> } >
<Route element={<Home />} />
<Route path="home-investor" element={<HomeInvestor />} />
<Route path="blogs" element={<Blogs />} />
<Route path="create-project" element={<CreateInvestmentProject />} />
<Route path="project/:id/start" element={<StartInvestmentProject />} />
<Route path="project/:id/invest" element={<SendInvestmentDeposit />} />
Route>
Routes>
Router>
WalletContext.Provider>
Fragment>
);
}
همانطور که مشاهده می کنید ، از مؤلفه روتر React برای تعریف درخت مسیرها استفاده می کند.
مشکل
من از REACT-ROUTER برای مدیریت ناوبری REACT SPA در قلاب UseNavigate استفاده می کنم. این یک روش کارآمد برای حرکت بین مسیرها است و مانند جذابیت کار می کند.
مشکل این است که ، همانطور که من در حال ادغام در یک برنامه Symfony هستم ، اگر برنامه را بارگیری مجدد کنم (در مرورگر) ، یک خطای Symfony دریافت می کنم که به من می گوید چنین مسیری وجود ندارد. این طبیعی است زیرا مسیرها در React تعریف شده اند ، نه در Symfony ، بنابراین روتر Symfony نمی تواند آنها را پیدا کند.
یک راه حل جزئی
اولین کاری که من در مورد آن فکر کردم این بود که اجازه می دهم یک کنترل کننده “Get_app” را به صورت این کار کند تا اینگونه به نظر برسد:
#[Route('/app', name: 'get_app', methods: ['GET'])]
#[Route('/app/{slug}', name: 'get_app_with_slug', methods: ['GET'])]
public function getApp(): Response
{
return $this->render('App.html.twig');
}
این زمانی کار می کند که مقدار slug یک رشته ساده مانند این است:
https://127.0.0.1:8000/app/home-investor
اما روتر Symfony در صورتی که قسمت Slug شامل مسیری با بخش های اضافی باشد خطایی خواهد داشت:
https://127.0.0.1:8000/app/project/2/invest
این امر به این دلیل است که مسیر “/app/{slug}” فقط یک بخش واحد را بعد از “/برنامه/” ضبط می کند. این بدان معنی است که هر URL با بیش از یک بخش پس از “/برنامه/” با این مسیر مطابقت نخواهد داشت. به عنوان مثال ، در URL “https://127.0.0.1:8000/app/project/2/invest” ، سه بخش بعد از “/app/” (پروژه ، 2 ، سرمایه گذاری) وجود دارد که باعث نمی شود که مطابقت نداشته باشد تعریف مسیر
راه حل
راه حلی که من در نهایت انتخاب کردم ، شامل یک سری تغییرات هم در قسمت پس زمینه (Symfony) و هم در قسمت جلوی (React) بود. بیایید با قسمت Symfony شروع کنیم.
تغییرات در قسمت Symfony
اولین تغییر قسمت Symfony ایجاد مشترک هسته برای گرفتن درخواست ورودی با استفاده از Symfony بود
هسته :: درخواست رویداد بیایید کد را ببینیم:
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onRequest'
];
}
public function onRequest(RequestEvent $event): void
{
$request = $event->getRequest();
if (preg_match('#^\/app\/#', $request->getRequestUri())) {
$pathSlug = preg_replace('#^\/app\/#', '', $request->getRequestUri());
$url = $this->router->generate('get_app', ['qslug' => $pathSlug]);
$event->setResponse(new RedirectResponse($url));
}
}
در در حال حاضر عملکرد شیء درخواست Symfony را دریافت می کند و “مسیر مسیر React” را از درخواست URI استخراج می کند. سپس ، مسیر “get_app” را ایجاد می کند که پارامتر آن به نام “qslug” با مقدار مسیر مسیر React عبور می کند.
سرانجام ، این پاسخ جدید به این رویداد را به عنوان Symfony redirectresponse تنظیم می کند.
تغییر دوم شامل اصلاح کنترلر “get_app” است به گونه ای که مسیر مسیر React را به سمت آن منتقل کند app.html.twig پرونده
#[Route('/app', name: 'get_app', methods: ['GET'])]
public function getApp(#[MapQueryParameter] ?string $qslug): Response
{
return $this->render('App.html.twig', ['pathSlug' => $qSlug]);
}
سرانجام ، پرونده Twig نیز باید Slug را به مؤلفه برنامه React منتقل کند.
{{ react_component('App', {pathSlug: pathSlug}) }} >
تغییرات در قسمت React
اکنون در قسمت React ، من باید بررسی کنم که آیا این مسیر دارای ارزش است یا خیر ، اگر چنین است ، به آن بروید.
اول از همه ، من قلاب به نام “userEloadEdroute” ایجاد کردم:
export const useReloadedRoute = (path?: string) => {
if(path){
localStorage.setItem('route_to_redirect', path);
}
const getRouteToNavigate = () => {
return localStorage.getItem('route_to_redirect');
}
const removeRouteToNavigate = () => {
localStorage.removeItem('route_to_redirect');
}
return {
getRouteToNavigate,
removeRouteToNavigate
};
}
قلاب مسیر را در محل محلی ذخیره می کند و تابعی را برای دستیابی به مسیر هدایت و دیگری برای حذف آن فراهم می کند.
سپس ، من یک زمینه React را در AppComponent ایجاد کردم تا مسیر بارگیری مجدد را ارائه دهم.
export const ReloadRouteContext = createContext<ReloadedRoute>(null);
interface AppProps {
pathSlug?: string
}
export default function App(props: AppProps) {
const wallet: Wallet = useStellarWallet(WalletNetwork.TESTNET);
const reloadedRoute: ReloadedRoute = useReloadedRoute(props?.pathSlug);
return (
<Fragment>
<CssBaseline />
<ReloadRouteContext.Provider value={reloadedRoute} >
<WalletContext.Provider value={wallet}>
<Router>
<Routes>
<Route path="/login" element={<SignIn />} />
<Route path="/app" element={<ProtectedRoute children={<Layout /> } /> } >
<Route element={<Home />} />
<Route path="home-investor" element={<HomeInvestor />} />
<Route path="blogs" element={<Blogs />} />
<Route path="create-project" element={<CreateInvestmentProject />} />
<Route path="project/:id/start" element={<StartInvestmentProject />} />
<Route path="project/:id/invest" element={<SendInvestmentDeposit />} />
Route>
Routes>
Router>
WalletContext.Provider>
ReloadRouteContext.Provider>
Fragment>
);
}
با تشکر از reoadroutecontext، من آماده بودم تا مسیر تغییر مسیر را در آن بررسی کنم مؤلفه چیدمانبشر
const reloadedRoute: ReloadedRoute = useContext(ReloadRouteContext);
useEffect(() => {
const rlRoute = reloadedRoute.getRouteToNavigate();
if (rlRoute) {
const path = '/app/' +rlRoute;
reloadedRoute.removeRouteToNavigate();
navigate(path);
}
}, []);
حال ، اگر مسیری برای تغییر مسیر ذخیره شده وجود داشته باشد ، Component LayoutComponent آن را از محل ذخیره خارج کرده و به سمت آن حرکت می کند.
پایان
این روشی است که من این مسئله را حل کردم. امیدوارم که اگر شما همان مشکل را تجربه کرده اید ، این راه حل می تواند به شما کمک کند. البته ، اگر راه بهتری برای دستیابی به آن می دانید ، لطفاً در نظرات به من اطلاع دهید تا بتوانم آن را آزمایش کنم :).
اگر محتوای من را دوست دارید و این ارزش را برای شما فراهم می کند ، خواندن کتاب من را در نظر بگیرید: ساختن یک API عملیاتی با استفاده از PHP و چارچوب Symfony