Description
Current Situation
Currently, sites built in IDOM are not SEO compatible. This is a fairly common issue with JavaScript frameworks such as ReactJS.
This might ultimately relate to persistent components (#34).
Proposed Actions
To resolve this, there needs to be an initial HTTP render, followed by a ReactPy re-render. The best way of doing this requires some form of persistent storage of all hook states, due to the fact that ASGI websockets are a completely different stack than HTTP rendering. Hook states will need to be stored server side in order to prevent spoofing. We likely have to use a database.
- Use the template tag to render the initial component as raw HTML
- Serialize all the component's hook values (probably through
dill.pickle
) and- This might need exposing some new hook APIs in core that provides returns hook values for a given component instance.
- Store serialized hook values within the database
- Use the component's UUID as the database ID.
- When the JavaScript client requests it, rehydrate the components hook values
- Execute the ReactJS rendering
- Do Not Unmount Old View On Reconnect reactpy#760 will need to be resolve prior to this
- Delete the component hook values from the database, as they are no longer needed.
The database model might look like this:
class IdomHookState(Model):
uuid = models.UUIDField(
primary_key=True, default=uuid.uuid4, editable=False, unique=True
)
hook_attributes = models.TextField()
This design brings up a challenge of determining when to evict old hook states (for example, if a user did the initial render but for some reason never performed a websocket connection). We don't want to store everything forever, so some configurable age-based eviction strategy might be needed. Expired entries should be culled before each fetch of IdomHookState.hook_attributes
. Expiration should be based on last access time, which is fairly simple to do in Django like such: last_accessed = models.DateTimeField(auto_now=True)
.