Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add request context and props to custom renderer #373

Merged
merged 1 commit into from
Aug 6, 2024

Conversation

crgwbr
Copy link
Contributor

@crgwbr crgwbr commented Jul 26, 2024

Ran into a case where I need request context data in our custom SSR renderer function. Specifically, I need this for loading translated strings from context into ttag—but this seems like it could be pretty broadly useful beyond that one use case.

@andreighh
Copy link

I'm really curious about how you handle i18n, if you don't mind giving some details, I was thinking about sending the texts from the view since #320 was merged, a bit annoying, I agree, but it would allow me to use django-rosetta for all texts so translators can translate directly in the admin.

@crgwbr
Copy link
Contributor Author

crgwbr commented Jul 31, 2024

@andreighh We have a context processor based on Django's JSON Catalog view. Something like this:

from typing import List, TypedDict, Union

from django.http import HttpRequest
from django.utils.translation import get_language
from django.utils.translation.trans_real import DjangoTranslation
from django.views.i18n import JSONCatalog


class CatalogEntry(TypedDict):
    key: str
    value: str


class TranslationCatalog(TypedDict):
    language: str
    catalog: List[CatalogEntry]


class TranslationProcessor(TypedDict):
    i18n: TranslationCatalog


def translation(request: HttpRequest) -> TranslationProcessor:
    catalog = JSONCatalog()
    locale: str = get_language()
    catalog.translation = DjangoTranslation(locale, domain=catalog.domain)
    context = catalog.get_context_data()
    return TranslationProcessor(
        i18n=TranslationCatalog(
            language=locale,
            catalog=[
                CatalogEntry(
                    key=k,
                    value=v,
                )
                for k, v in context["catalog"].items()
            ],
        ),
    )

That gets our JS translations into context. Then we can use them with a translation function like ttag or your own implementation of Django's gettext function.

@andreighh
Copy link

Thank you!

@crgwbr
Copy link
Contributor Author

crgwbr commented Aug 5, 2024

@silviogutierrez Any thoughts/concerns on this?

@silviogutierrez
Copy link
Owner

@crgwbr : I added comments/changes a while back. Did GitHub mess up the notifications?

@crgwbr
Copy link
Contributor Author

crgwbr commented Aug 5, 2024

@silviogutierrez I don't see any comments anywhere on here. Must have gotten lost somewhere.

Copy link
Owner

@silviogutierrez silviogutierrez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so "Pending" means "pending submission" not "pending fixes".

@@ -6,7 +6,13 @@ export type Options = {
client?: (options: ClientConfig) => InlineConfig;
renderer?: (options: RendererConfig) => InlineConfig;
};
render?: (content: JSX.Element) => Promise<JSX.Element>;
render?: (
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we actually use the rest of the Options object, looking around. Just Renderer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Done

context: any;
props: any;
},
) => Promise<JSX.Element>;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Renderer should be:

  1. Generic, `Renderer<T = unknown>'.
  2. Context should be T, props should always be unknown.
  3. In the library code, use Renderer without a generic param.
  4. But in the custom implementation, have @reactivated / generator.mts output Renderer bound as Renderer<_Types["Context"]>

Hope that makes sense.

My client/renderer.tsx looks like this, for reference, so the generic would be seamless and type if further:

import React from "react";

import {Renderer} from "@reactivated";

export default (async (content) => {
    const {makeStore} = await import("@client/store");
    const store = makeStore();
    const {Provider} = await import("react-redux");
    const {resetServerContext} = await import("react-beautiful-dnd");
    resetServerContext();

    return <Provider store={store}>{content}</Provider>;
}) satisfies Renderer;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Done

@crgwbr crgwbr force-pushed the feat_renderer_context branch 2 times, most recently from 6049424 to 810c01b Compare August 6, 2024 13:23
@silviogutierrez silviogutierrez changed the title Pass request props and context into custom renderer functions Pass request context and props into custom renderer Aug 6, 2024
@silviogutierrez silviogutierrez changed the title Pass request context and props into custom renderer Add request context and props to custom renderer Aug 6, 2024
@kodiakhq kodiakhq bot merged commit 24f3480 into silviogutierrez:main Aug 6, 2024
11 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Aug 6, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants