Emotion: Functions that are interpolated in css calls will be stringified.

Created on 7 Dec 2018  Â·  9Comments  Â·  Source: emotion-js/emotion

  • emotion version: 10.0.2
  • react version: 16.3

Relevant code.

// @flow
import { type SerializedStyles } from '@emotion/utils';
import themeData from './theme';

// Use this function in your components to provide a fallback theme in case
// no ThemeProvider is available
const withFallback = (fn: Object => SerializedStyles) => ({
  theme,
  ...props
}: {
  theme: Object,
}) => {
  const t = Object.keys(theme).length ? theme : themeData.light;
  return fn({ theme: t, ...props });
};

export default withFallback;

What you did:

I created this function to be used in my UI library to make sure a theme is always defined
even if no ThemeProvider is available.

Usage:

css`
  color: ${withFallback(props => props.theme.primary)};
`

What happened:

When I run it, in some contexts, I get this error:

console.error node_modules/@emotion/serialize/dist/serialize.cjs.dev.js:135
      Functions that are interpolated in css calls will be stringified.
      If you want to have a css call based on props, create a function that returns a css call like this
      let dynamicStyle = (props) => css`color: ${props.color}`
      It can be called directly with props or interpolated in a styled call like this
      let SomeComponent = styled('div')`${dynamicStyle}`

and the resulting CSS, in fact, is the stringified function.

Reproduction:

https://github.com/FezVrasta/emotion-interpolation-repro

Just run the tests to see the error

Problem description:

My withFallback function seems to work fine if used as:

css`
  color: ${withFallback(props => props.theme.primary)};
`

but throws error if used as:

const textStyles = () => css`
  color: ${withFallback(props => props.theme.primary)};
`;

css`
  ${textStyles()}
`;

(I also tried:

const textStyles = () => () => css`
  color: ${withFallback(props => props.theme.primary)};
`;

with the same result)

Suggested solution:

Not sure

Most helpful comment

what about conditionals such as

 const disableStyle = props => css`
 color: ${props => (props.disabled ? props.theme.colors.disabled : null)};

All 9 comments

I found out the minimum reproducible example:

// @flow
import styled from '@emotion/styled';
import { css } from '@emotion/core';

const color = css`
  color: ${props => props.color};
`;

const App = styled.div`
  ${color};
`;

export default App;
console.error node_modules/@emotion/serialize/dist/serialize.cjs.dev.js:135
      Functions that are interpolated in css calls will be stringified.
      If you want to have a css call based on props, create a function that returns a css call like this
      let dynamicStyle = (props) => css`color: ${props.color}`
      It can be called directly with props or interpolated in a styled call like this
      let SomeComponent = styled('div')`${dynamicStyle}`

Just for completeness, my use case is the following:

import { css } from '@emotion/core';

const styles = {
  a: props => css`
    color: ${props.a};
  `,
  b: props => css`
    color: ${props.b};
  `,
};

export const textStyles = (...ss) => props => ss.map(s => styles[s]);

test

it('it does not break', () => {
  expect(
    css`
      ${textStyles('a', 'b')};
    `.styles
  ).toMatchSnapshot();
});

edit
I also noticed some inconsistency between css and styled, the function above renders the expected CSS when used inside styled.div but renders the stringified function if used inside css. But in both cases I get the message about functions getting stringified.

Css works also in non-react scenarios, it doesnt know anything about things
like props. Just call this function with props urself and interpolate the
result instead of interpolating the function itself. Interpolated functions
are called with props only when interpolated into styled call, not css call
On Fri, 7 Dec 2018 at 18:21, Federico Zivolo notifications@github.com
wrote:

Just for completeness, my use case is the following:

import { css } from '@emotion/core';

const styles = {
a: props => css color: ${props.a}; ,
b: props => css color: ${props.b}; ,
};

export const textStyles = (...ss) => props => ss.map(s => styles[s]);

test

it('it does not break', () => {
expect(
css ${textStyles('a', 'b')}; .styles
).toMatchSnapshot();
});

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/emotion-js/emotion/issues/1085#issuecomment-445303213,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AJWMkoabMhABofk7ormbnoADs-itmV8Eks5u2qOTgaJpZM4ZIhqt
.

I see, but I still get the same error message (even tho, it works) if I use styled with some default props. Test out my latest example with this test:

it('works with "withFallback" powered styles', () => {
  const T = styled.div`
    ${textStyles('secondary', 'disabled', 'link', 'highlighted')}
  `;
  expect(mount(<T />)).toMatchSnapshot();
});
console.error node_modules/@emotion/serialize/dist/serialize.cjs.dev.js:135
      Functions that are interpolated in css calls will be stringified.
      If you want to have a css call based on props, create a function that returns a css call like this
      let dynamicStyle = (props) => css`color: ${props.color}`
      It can be called directly with props or interpolated in a styled call like this
      let SomeComponent = styled('div')`${dynamicStyle}`

Ok nevermind, it was something wrong with my dirty setup, it actually works inside styled. Thanks for the support!

@FezVrasta I'm having the same issue. What was the issue with your setup?

This issue still ranks high on search engines, so here's a solution.

// @flow
import styled from '@emotion/styled';
import { css } from '@emotion/core';

- const color = css`
+ const color = props => css`
-  color: ${props => props.color};
+  color: ${props.color};
`;

const App = styled.div`
  ${color};
`;

export default App;

what about conditionals such as

 const disableStyle = props => css`
 color: ${props => (props.disabled ? props.theme.colors.disabled : null)};

This is what I do or plan to do: https://codesandbox.io/s/determined-morse-oehqv

export const get = (props) => (key) => props[key];

export const styleWithHelpers = (fn) => (props) => {
  return fn({ get: get(props) }, props);
};

---------

import styled from '@emotion/styled';
import { css } from '@emotion/core';
import { styleWithHelpers } from 'some-module';

const color = styleWithHelpers(({ get }) => css`
  color: ${get('color')};
`);

const App = styled.div`
  ${color};
`;

export default App;
Was this page helpful?
0 / 5 - 0 ratings