xjpro / react-in-angularjs

A super simple way to render React components in AngularJS
The Unlicense
23 stars 13 forks source link

angularizeDirective broken in 18.0.0 #28

Open rofreytag opened 1 year ago

rofreytag commented 1 year ago

hey there, I noticed that angularizeDirective does not work anymore:

this code

      link: function (scope, element) {
        // Add $scope
        scope.$scope = scope;
        const root = ReactDOMClient.createRoot($element[0]);

references $element, instead of using element

additionally, the onDestroy and watchGroup methods can not reference this.root as it is never created.

Here is my fixed version:

import React from "react";
import { createRoot } from 'react-dom/client';

// fixed version of react-in-angular angularizeDirective
// - the linker function is broken in 18.0.0, referencing $element instead of element
// - the $watchGroup function is broken, referencing the wrong root element
// - if there is a patch to this library, this file can be removed and usage replaced with the libs version
export function angularizeDirectiveFixed(Component, directiveName, angularApp, bindings) {
  bindings = bindings || {};
  if (typeof window === "undefined" || typeof angularApp === "undefined")
    return;

  angularApp.directive(directiveName, function () {
    return {
      scope: bindings,
      replace: true,
      link: function (scope, element) {
        // Add $scope
        scope.$scope = scope;

        // THIS is fix 1
        const root = createRoot(element[0]);
        this.root = root;

        // First render - needed?
        root.render(React.createElement(Component, scope));

        // Watch for any changes in bindings, then rerender
        const keys = [];
        for (let bindingKey of Object.keys(bindings)) {
          if (/^data[A-Z]/.test(bindingKey)) {
            console.warn(
              `"${bindingKey}" binding for ${directiveName} directive will be undefined because AngularJS ignores attributes starting with data-`
            );
          }
          if (bindings[bindingKey] !== "&") {
            keys.push(bindingKey);
          }
        }

        // THIS is fix 2
        scope.$watchGroup(keys, () => {
          root.render(React.createElement(Component, scope));
        });

        scope.$on("$destroy", function () {
          root.unmount();
        });
      },
    };
  });
}
SleepWalker commented 1 year ago

@rofreytag the line this.root = root; should be commented, right?

rofreytag commented 1 year ago

@SleepWalker I think you are right, this.root seems not referenced anymore