direflow icon indicating copy to clipboard operation
direflow copied to clipboard

Styled Components plugin makes components crash when a property is changed

Open kcmr opened this issue 3 years ago • 4 comments

Describe the bug
Components that use the Styled Components plugin crash when a property is changed. This only happens in production mode (direflow-scripts build). In development mode, when you change a property, the style tag is appended at a different position that it is on the initial render and the styles are not applied to the component anymore. The generated class names don't exist in the component's DOM, but also the CSS properties are not updated accordingly.

To reproduce
Steps to reproduce the behavior:

The behavior can be reproduced in this Direflow project: https://sample-direflow-project.vercel.app/

  1. Inspect the page and select <styled-component>
  2. Change its bgColor property to red: $0.bgColor = 'red'
  3. See error

Project's repo: https://github.com/kcmr/sample-direflow-project

Expected behavior
When you change a property, the updated styles are applied and the component doesn't crash.

Package Manager:
npm

Screenshots

Production mode Captura de pantalla 2021-02-23 a las 11 15 27

Development mode on initial render before-changing-prop-initial-render

Development mode after changing a prop dev-mode-after-changing-prop

kcmr avatar Feb 23 '21 10:02 kcmr

I'm also coming up against this issue. Is there any progress on this or any idea why this is happening? Any workarounds?

KitoC avatar May 26 '21 23:05 KitoC

Found a workaround by wrapping web-components in a HOC. The weird thing is that the web-component will only accept props if they are defined on .defaultProps of web-component.

const withStyledComponents = function (Component: any) {
  const StyleSheetedComponent = ({ children, insertionPoint }: any) => {
    return (
      /* @ts-ignore */
      <StyleSheetManager target={insertionPoint}>{children}</StyleSheetManager>
    );
  };

  return function (props: any) {
    const [mounted, setMounted] = useState(false);
    const insertionPoint = useRef(null);

    useEffect(() => {
      setMounted(true);
    }, []);

    const strippedProps: any = {};

    Object.entries(props).forEach(([key, value]) => {
      if (`${props[key]}` !== "undefined") {
        strippedProps[key] = value;
      }
    });

    return (
      <>
        <span ref={insertionPoint} id="styled-components-ref"></span>
        {insertionPoint.current && (
          <StyleSheetedComponent insertionPoint={insertionPoint.current}>
            <Component {...strippedProps} />
          </StyleSheetedComponent>
        )}
      </>
    );
  };
};

KitoC avatar May 27 '21 03:05 KitoC

Hi @brentonr94, the trick is to wrap the component you created before assigning the defaultProps. e.g.

const Button = withStyledComponents((props) => {
  const { onClick } = props;

  return (
    <StyledButton onClick={onClick}>
      <slot key="children" name="children" />
    </StyledButton>
  );
});

Button.defaultProps = { onClick: null };

export default Button;

KitoC avatar Jul 27 '21 06:07 KitoC

Great thank you! I was just reworking my comment as I found defining the props in the direflow config also resolved my issue of props not being passed through. Thanks again!

brento1 avatar Jul 27 '21 06:07 brento1