Thursday 5 September 2019

Dependency cycle issue when using Styled Components alongside compound component pattern

I have a few accordion components created using the compound component methodology (there is a great talk by Ryan Florence describing compound components here).

One of the ESLint rules I have setup is import/no-cycle to prevent dependency cycles. As I'm using the compound component methodology as well as the advised way to refer to other components in Styled Components by keeping all styles relevant to a particular component in that file itself, I am triggering the import/no-cycle warning.

Here is my Accordion.js file.

import React, { useState } from "react";

import AccordionTrigger from "./accordionTrigger";
import AccordionContent from "./accordionContent";

const Accordion = ({ children, show }) => {
  const [isActive, setIsActive] = useState(show);

  const handleTriggerClick = () => {
    setIsActive(!isActive);
  };

  const compoundChildren = React.Children.map(children, child => {
    switch (child.type) {
      case AccordionTrigger:
        return React.cloneElement(child, {
          onClick: handleTriggerClick,
          active: isActive ? 1 : 0,
        });

      case AccordionContent:
        return React.cloneElement(child, {
          show: isActive,
        });

      default:
        return child;
    }
  });

  return <div show={show ? 1 : 0}>{compoundChildren}</div>;
};

export default Accordion;

And my AccordionTrigger.js file.

import React from "react";
import styled from "styled-components";

import FauxButton from "../buttons/fauxButton";

import Accordion from "./accordion";
import TopLevelTrigger from "./topLevelTrigger";
import SecondaryLevelTrigger from "./secondaryLevelTrigger";

const Root = styled(FauxButton)`
  ${Accordion} & {
    width: 100%;
    border-bottom: 1px solid ${p => p.theme.greyLight};
  }
`;

const AccordionTrigger = ({ active, children, ...rest }) => {
  const clonedChildren = React.Children.map(children, child => {
    switch (child.type) {
      case TopLevelTrigger:
      case SecondaryLevelTrigger:
        return React.cloneElement(child, {
          active,
        });

      default:
        return child;
    }
  });
  return <Root {...rest}>{clonedChildren}</Root>;
};

export default AccordionTrigger;

One thing I have tried is defining the AccordionTrigger styles in Accordion.js like so...

const Root = styled.div`
  ${AccordionTrigger} & {
    width: 100%;
    border-bottom: 1px solid ${p => p.theme.greyLight};
  }
`;

const Accordion = ({ children, show }) => {
  ...same logic as before here

  return <Root show={show ? 1 : 0}>{compoundChildren}</Root>;
};

...but this doesn't work, the styles simply don't get added to the AccordionTrigger component. I know I can simply add my own class via the className prop when cloning inside of the Accordion component and then refer to it that way but I was wondering if there was a way to prevent this?



from Dependency cycle issue when using Styled Components alongside compound component pattern

No comments:

Post a Comment