formatjs / formatjs

The monorepo home to all of the FormatJS related libraries, most notably react-intl.

Home Page:https://formatjs.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

react-intl 6.3.0 breaks react widgets embedded into preact pages

dkreft-termly opened this issue · comments

Which package?

react-intl

Describe the bug

When a page that is rendered with Preact (and uses react-intl), and a widget that was created using React (and also uses react-intl) is injected into it, the following error is reported in the console:

debug.js:172 Uncaught Error: Invalid type passed to createElement(): [object Object]

When the host website uses react-intl@6.2.10, all works as expected. It's only when the Preact site is updated to use react-intl@6.3.0 (or later) that this error occurs.

To Reproduce

Reproducible Steps/Repo

I've attached preact-test.tgz to this bug report. It contains a simple Preact website (created with Vite), and a pre-built widget in public/react-widget.js (created with create-react-app).

This is the "happy case":

  1. $ tar zxvf preact-test.tgz
  2. $ cd preact-test
  3. $ npm i && npm run dev

This is the failure case:

  1. $ git co 28942ca
  2. $ rm -rf node_modules
  3. $ npm i && npm run dev

Expected behavior

I would expect the both the app and the widget should happily render their respective contents, and no errors should be spat to the console, even in versions >= 6.3.0.

Screenshots

Happy Case

image

Failure Case

image

Desktop:

  • OS: MacOS Sonoma (14.2.1)
  • Browser: Chrome
  • Version 120.0.6099.199

Smartphone:

N/A

Additional context

I believe this is caused by the inclusion of a global cache at https://github.com/formatjs/formatjs/compare/react-intl@6.2.10...react-intl@6.3.0#diff-063c980f078c6afe2a602406195c13c033220a103e9b80ac45ecffca4f15d13c

If you'd like the source for the React widget, download react-widget.tgz and execute the following:

  1. $ tar zxvf react-widget.tgz
  2. $ cd react-widget
  3. $ npm i && npm build:widget

This will create widget/index.js. This is what appears in the preact-test repo under public/react-widget.js

can u make a codesandbox?

can u make a codesandbox?

Hi, @longlho.

My employer was gracious enough to allow me to take the better portion of my day just to give you two Git repos that you can use to reproduce the error in a minimalistic manner, and I'd rather not have to lose another day of productivity trying to figure out how to reproduce this all in CodeSandbox. Why do you feel that what I've given you is insufficient to work with?

@dkreft-termly Downloading files randomly aren't safe from the security point of view; hence he recommended codesandbox or a git repo.

Onto the issue, after a quick search, this could just be on the preact side, or the bundling side. Note that preact isn't exactly react which react-intl fully supports

@cungminh2710 Then download and unpack them in a Docker container. :-)

I honestly don't even know how to go about reproducing this in the context of a CodeSandbox, and I really cannot afford to spend the time to figure it out right now.

What I can tell you, however, is that the actual setup is rather trivial...you simply set up a React component in both a Preact project and in a straight React project:

import { IntlProvider } from 'react-intl'

export function App() {
  return (
    <IntlProvider locale={ navigator.language }>
      Hello, world
    </IntlProvider>
  )
}

You then build the React-based widget:

    "build:widget": "npm run build && npm run parcel build src/index.js --no-source-maps -d widget",

Take the output file and inject that into the Preact site using a <script> tag:

<!doctype html>
<html lang="en">
  <head>
    <title>react-intl 6.3.0 bug test</title>
  </head>

  <body>
    <div id="app">
      <div id="widget-root"></div>
    </div>

    <script type="module" src="/src/main.jsx"></script>

    <script
      type="text/javascript"
      src="/react-widget.js"
    ></script>
  </body>
</html>

The core problem here, as I pointed out in the description, is the (perhaps naive) use of a global variable (window.__REACT_INTL_CONTEXT__) to act as a cache for ReactIntl. I say "naive" because it assumes that the context that is returned by React.createContext() is going to be compatible for all applications/widgets running on the page. I'm sure you can appreciate the dangers involved in using a global cache in an environment where you don't have ultimate control over the versions of third-party libraries that are in use on a page.

Below is a CodeSandbox to repro the problem. I basically just took the repo that I'd originally gzipped and attached to this ticket and imported it into CodeSandbox, made a tweak and it was done.

Right now, it's set up to use react-intl@6.3.0 (the error case).

https://codesandbox.io/p/github/dkreft-termly/react-intl-bug/master?layout=%257B%2522sidebarPanel%2522%253A%2522GIT%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clrpn8w9q00063b6qcvveyjn1%2522%252C%2522sizes%2522%253A%255B70%252C30%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clrpn8w9q00023b6qa72nu4kz%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clrpn8w9q00043b6qz0l1djjr%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clrpn8w9q00053b6q8l8p15uu%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clrpn8w9q00023b6qa72nu4kz%2522%253A%257B%2522id%2522%253A%2522clrpn8w9q00023b6qa72nu4kz%2522%252C%2522activeTabId%2522%253A%2522clrpnk5nh01ih3b6qgrw4ezs0%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clrpn8w9p00013b6qktanrt49%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252F.gitignore%2522%257D%252C%257B%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252F.codesandbox%252Ftasks.json%2522%252C%2522id%2522%253A%2522clrpnk3dx01gv3b6qhvutb8fg%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522DIFF%2522%252C%2522filepath%2522%253A%2522%252F.codesandbox%252Ftasks.json%2522%252C%2522base%2522%253A%2522HEAD%2522%252C%2522id%2522%253A%2522clrpnk5nh01ih3b6qgrw4ezs0%2522%252C%2522mode%2522%253A%2522temporary%2522%257D%255D%257D%252C%2522clrpn8w9q00053b6q8l8p15uu%2522%253A%257B%2522id%2522%253A%2522clrpn8w9q00053b6q8l8p15uu%2522%252C%2522activeTabId%2522%253A%2522clrpnjye701d53b6qb07adhjo%2522%252C%2522tabs%2522%253A%255B%257B%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A5173%252C%2522id%2522%253A%2522clrpnhhx001243b6qph55xnkf%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522path%2522%253A%2522%252F%2522%257D%252C%257B%2522type%2522%253A%2522TASK_PORT%2522%252C%2522taskId%2522%253A%2522dev%2522%252C%2522port%2522%253A5173%252C%2522id%2522%253A%2522clrpnjye701d53b6qb07adhjo%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522path%2522%253A%2522%252F%2522%257D%255D%257D%252C%2522clrpn8w9q00043b6qz0l1djjr%2522%253A%257B%2522id%2522%253A%2522clrpn8w9q00043b6qz0l1djjr%2522%252C%2522activeTabId%2522%253A%2522clrpn8yvd004n3b6qr3j5szjz%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clrpn8w9q00033b6q20vq2nqv%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TERMINAL%2522%252C%2522shellId%2522%253A%2522clrpnfp190013e8hkaanv0iel%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522dev%2522%252C%2522id%2522%253A%2522clrpn8yvd004n3b6qr3j5szjz%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522CSB_RUN_OUTSIDE_CONTAINER%253D1%2520devcontainer%2520templates%2520apply%2520--template-id%2520%255C%2522ghcr.io%252Fdevcontainers%252Ftemplates%252Fjavascript-node%255C%2522%2520--template-args%2520%27%257B%257D%27%2520--features%2520%27%255B%255D%27%2522%252C%2522id%2522%253A%2522clrpna2xr008q3b6qs3kk2r5f%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D

@longlho @cungminh2710 Do you need anything further from me?

Thanks will take a look soon

looks like you can add <script>window.__REACT_INTL_BYPASS_GLOBAL_CONTEXT__ = true</script> to your index.html to disable that behavior

For posterity's sake, #4125 addresses this issue.

When I get a chance, I'll add this and see if it works for my situation.

@longlho I'm a bit perplexed by how this issue was handled. I described the situation in detail, and even pointed you directly to the PR that introduced this issue. Your response, in the end, was to simply mention the addition of another global variable which was introduced in a PR that I had to go look up myself. Why did you have my jump through all of the extra hoops to set up a code sandbox when all the information you needed to point me to the solution was right at your fingertips the whole time?

@longlho By the way, I don't see any mention of this in the react-intl documentation. Am I missing something?