код обертки компонента сервера для каждой страницы:
Код: Выделить всё
const getSSRResult = async (
seed: SSRHandlerSeed,
): Promise => {
if (SSRUtil.SSR_FETCH_ENABLED && seed) {
const { logged } = await SSRUtil.getAuthInfoFromCookie();
const rsc = await SSRUtil.isRSC();
if (!logged && !rsc) {
const handler = getSSRHandlerHubItem(seed.key).handler(seed.arg);
try {
const payload = await handler.fetch();
return { payload };
} catch (e) {
const errorData = getErrorData(e);
LogManager.error(
new Date().toLocaleTimeString() +
' ' +
'Error fetching SSR data (key=' +
seed.key +
')',
errorData,
e,
);
}
}
}
return undefined;
};
const SSRWrapper = async ({
seed,
children,
}: {
seed: SSRHandlerSeed;
children: React.ReactNode;
}) => {
const result = await getSSRResult(seed);
return (
{children}
);
};
export default SSRWrapper;
< /code>
, который вызывает клиентский компонент внутренней обертки: < /p>
const useDispatch = (params: {
seed: SSRHandlerSeed;
result: { payload: TResult } | undefined;
}) => {
const { seed, result } = params;
const definedRef = React.useRef(false);
const layoutContext = useLayoutContext();
const store = useStore();
const { path } = useMainRouter();
const afterWait = React.useRef(false);
let waitDispatch = false;
if (!definedRef.current && path && layoutContext && result) {
definedRef.current = true;
if (path !== layoutContext.pathname) {
layoutContext.pathname = path;
const handler = getSSRHandlerHubItem(seed.key).handler(seed.arg);
if (handler.selectToIgnore) {
const state = handler.selectToIgnore(store.getState());
waitDispatch = state?.complete ?? false;
}
if (!waitDispatch) {
handler.dispatch({ store, data: result.payload });
}
}
}
React.useEffect(() => {
if (waitDispatch && !afterWait.current && result) {
afterWait.current = true;
const handler = getSSRHandlerHubItem(seed.key).handler(seed.arg);
handler.dispatch({ store, data: result.payload });
}
}, [waitDispatch, seed, result, store]);
};
const SSRClientWrapper = ({
seed,
result,
children,
}: {
seed: SSRHandlerSeed;
result: { payload: TResult } | undefined;
children: React.ReactNode;
}) => {
useDispatch({ seed, result });
return {children};
};
export default SSRClientWrapper;
< /code>
Для страниц, которые нуждаются в SSR, у меня есть определения, такие как: < /p>
//...
const privacyPolicy: SimpleDynamicSSRHandler<
void,
{ appInfo: AppInfo; result: GeneralInfo },
GeneralInfoOuterState
> = () => ({
fetch: async () => {
const appInfo = await apiInternalGetAppInfo();
const result = await apiInternalGetPrivacyPolicy();
return { appInfo, result };
},
dispatch: ({ store, data: { appInfo, result } }) => {
store.dispatch(retrieveAppInfo.fulfilled(appInfo, nanoid()));
store.dispatch(fetchPrivacyPolicy.fulfilled(result, nanoid()));
},
selectToIgnore: selectPrivacyPolicyInfoState,
});
const termsOfUse: SimpleDynamicSSRHandler<
void,
{ appInfo: AppInfo; result: GeneralInfo },
GeneralInfoOuterState
> = () => ({
fetch: async () => {
const appInfo = await apiInternalGetAppInfo();
const result = await apiInternalGetTermsOfUse();
return { appInfo, result };
},
dispatch: ({ store, data: { appInfo, result } }) => {
store.dispatch(retrieveAppInfo.fulfilled(appInfo, nanoid()));
store.dispatch(fetchTermsOfUse.fulfilled(result, nanoid()));
},
selectToIgnore: selectTermsOfUseInfoState,
});
//...
< /code>
Компонент сервера в верхней части, который представляет страницу (page.tsx), как: < /p>
const Page: React.FC = async () => (
);
export default Page;
< /code>
TermsPageМой хранилище находится внутри макета. Privacypolicy страница через URL, все работает нормально. Если я перейду к Termonofuse < /code> в первый раз, он регистрирует ошибку: < /p>
store.ts:140 не может обновить компонент (
Код: Выделить всё
Main< /code>) при рендеринге
различный компонент (SSRClientWrapperвызов внутри ssrClientWrapper , следуйте The Stack Trace, как описано в
https://react.dev/link/setState-in-renderобразное/>
Я знаю, что эта ошибка. Синхронно во время рендеринга.
Я использовал документы Redux по адресу https://redux.js.org/usage/nextjs#loading-initial-data в качестве ссылки для использования REF, чтобы избежать множественных диспетчеров. Ошибка, и первая нагрузка имеет SSR (поэтому, для целей SEO и OG -ссылок, она работает нормально), но когда пользователь перемещается, он увидит прядильщик/скелет на следующей странице, и он будет моргнуть, показывая содержимое (в то время как сейчас, с журналами ошибок, пользователь увидит содержание сразу после навигации). Кроме того, это приведет к тому, что это приведет к обращению к API на стороне клиента (навигация вызовет двойную выборку).
Я пытался с uselayouteffect вместо использования , но разница не было. В приведенном выше случае Privacypolicy использует селектор для Appinfo состояния, а Temerofuse обновляет его. Если я загружаю страницу, которая не зависит от состояния Appinfo , и перейти к Termofuse , ошибки не происходит. В приведенном выше случае, основной компонент в клиентском компоненте страницы Privacypolicy (не включенная в код выше) имеет селектор для Appinfo состояния. Производство, чтобы определить, загружается ли страница в первый раз для пользователя, или это внутренняя навигация, и в этом случае ничего не должно быть загружено (используя Fetch) на сервере (что также относится к пользователям, не являющимся автоэутированными пользователями). При этом, за исключением 1 -й нагрузки, я теряю преимущества кэша сервера и показывая страницу со всем контентом для пользователя, и вместо этого он видит прядильщик, и после завершения выбора он видит контент. По крайней мере, такого пути, двойной выборки (потому что на сервере нет никакого выгоды). Этот код уже присутствует в кодовых блоках, которые я опубликовал выше, но я бы предпочел, чтобы не было пропускать выборку на стороне сервера при навигации, и вместо этого он должен отображать загруженную страницу непосредственно пользователю после навигации.
Подробнее здесь: https://stackoverflow.com/questions/797 ... g-internal
Мобильная версия