Emotion: Cannot dynamically add emotion styling to a child component via React.cloneElement

Created on 13 Dec 2018  路  2Comments  路  Source: emotion-js/emotion

  • emotion version: 10.0.4
  • react version: 16.6.3

Relevant code:

let red = css`
    color: red;
`;
let green = css`
    color: green;
`;
let blue = css`
    color: blue;
`;

class Test2 extends React.Component {
    render() {
        let c = React.Children.map(this.props.children, (child) => (
            React.cloneElement(child, {css: red})
        ));
        console.log({props2: this.props}); // This shows children and nothing else

        return <div>
            <div css={[green]}>Test 2</div>
            {c}
        </div>;
    }
}
class Test3 extends React.Component {
    render() {
        console.log({props3: this.props}); // This shows the css object provided by Test2
        return <div css={[blue]}>Test 3</div>;
    }
}

const App = () => {
    return (
        <div>
            <div css={[red]}>Test</div>
            <Test2><Test3 /></Test2>
        </div>
    );
};

What you did:
I am attempting to migrate several libraries and an application to Emotion 10. A lot of the React components in this project are dynamically generated, and have to have styling provided from data sources. This worked fine when we used className, cx(), and cloneElement to inject these styles into child components.

What happened:
When I dynamically apply the css prop to a child component via cloneElement, it is not picked up or used. In the example code above, "Test 3" should come out red, but instead is blue. This is not a case of blue taking precedence; adding another style such as font-style: italic to red will make Test italic, but not Test 3.

Is there a way to fix this that does not involve using the legacy className solution?

Most helpful comment

css prop relies on jsx (which is an alternative to React.createElement), you'd have to do this:

import { jsx } from '@emotion/core'

const cloneElement = (element, props) =>
  jsx(element.type, {
    key: element.key,
    ref: element.ref,
    ...element.props
    ...props,
  });

// ...

let c = React.Children.map(this.props.children, (child) => (
    cloneElement(child, {css: red})
));

All 2 comments

css prop relies on jsx (which is an alternative to React.createElement), you'd have to do this:

import { jsx } from '@emotion/core'

const cloneElement = (element, props) =>
  jsx(element.type, {
    key: element.key,
    ref: element.ref,
    ...element.props
    ...props,
  });

// ...

let c = React.Children.map(this.props.children, (child) => (
    cloneElement(child, {css: red})
));

This worked, thank you!

Was this page helpful?
0 / 5 - 0 ratings