Original link:

Medium.com/styled-comp…

Styled components are becoming increasingly common in front-end development, especially in the React community, and styled components stand out for the following features:

  • It is based on tag template syntax
  • Styles are defined in the form of writing the React component
  • Resolve the modularity of the CSS
  • Provides functionality that CSS does not have, such as nesting
  • You do not need to configure the preceding features

Developers no longer have to think hard about CSS class names. So, how does all of this work?

Note: If you are not familiar with Styled – Components, read this documentation first

Ones grammar

We create a simple button using Styled – Components:

const Button = styled.button` color: coral; Padding: 0.25 rem 1 rem; border: solid 2px coral; border-radius: 3px; Margin: 0.5 rem; font-size: 1rem; `;
Copy the code

Online sample

Styled. Button is simply a shorthand for Styled (‘button’), and the Styled method takes a usable label name as an argument. If you’re familiar with tag templates, you know that a button is just a function that takes an array of strings as an argument. Take a look at this code:

const Button = styled('button') (['color: coral; ' +
  'the padding: 0.25 rem 1 rem; ' +
  'border: solid 2px coral; ' +
  'border-radius: 3px; ' +
  'margin: 0.5 rem; ' +
  'font-size: 1rem; '
]);
Copy the code

Online sample

As you can now see that the Styled is really a component factory, we can imagine how it is implemented.

Refactoring styled – components

const myStyled = (TargetComponent) = > ([style]) = > class extends React.Component {
  componentDidMount() {
    this.element.setAttribute('style', style);
  }

  render() {
    return (
      <TargetComponent {. this.props} ref={element= > this.element = element } />); }};const Button = myStyled('button')` color: coral; Padding: 0.25 rem 1 rem; border: solid 2px coral; border-radius: 3px; Margin: 0.5 rem; font-size: 1rem; `;
Copy the code

Online sample

The code implementation above looks simple — the myStyled factory function creates a new component based on the given tag name, setting the inline style after the component is mounted. But what if our component style depends on some props?

const primaryColor = 'coral';

const Button = styled('button')`
  background: ${({ primary }) => primary ? 'white ' : primaryColor};
  color: ${({ primary }) => primary ? primaryColor : 'white'}; Padding: 0.25 rem 1 rem; border: solid 2px${primaryColor}; border-radius: 3px; Margin: 0.5 rem; `;
Copy the code

To evaluate the interpolation in the style when the component is mounted or when the component props are updated, we need to update the implementation of the code above:

const myStyled = (TargetComponent) = > (strs, ... exprs) = > class extends React.Component {
  interpolateStyle() {
    const style = exprs.reduce((result, expr, index) = > {
      const isFunc = typeof expr === 'function';
      const value = isFunc ? expr(this.props) : expr;

      return result + value + strs[index + 1];
    }, strs[0]);

    this.element.setAttribute('style', style);
  }

  componentDidMount() {
    this.interpolateStyle();
  }

  componentDidUpdate() {
    this.interpolateStyle();
  }

  render() {
    return <TargetComponent {. this.props} ref={element= > this.element = element } />}};const primaryColor = 'coral';

const Button = myStyled('button')`
  background: ${({ primary }) => primary ? primaryColor : 'white'};
  color: ${({ primary }) => primary ? 'white' : primaryColor}; Padding: 0.25 rem 1 rem; border: solid 2px${primaryColor}; border-radius: 3px; Margin: 0.5 rem; font-size: 1rem; `;
Copy the code

Online sample

The trickiest part of the code above is how to get the style string:

const style = exprs.reduce((result, expr, index) = > {
  const isFunc = typeof expr === 'function';
  const value = isFunc ? expr(this.props) : expr;

  return result + value + strs[index + 1];
}, strs[0]);
Copy the code

We concatenate all the string fragments to get result after result; If an interpolation is a function type, it is passed the props of the component and called at the same time.

The simple factory above looks a lot like what styled- Components provides, but the underlying implementation of Styled – Components is actually more interesting: it does not use inline styles. Let’s approach styled- Components to see exactly what happens when the component is imported and created.

styled-componentsThe underlying principle

Introducing styled – components

When you first introduce the Styled – Components library, a counter variable is created internally to record each component created via the Styled factory function.

Call the styled. Tag-name factory function

const Button = styled.button`
  font-size: ${({ sizeValue }) => sizeValue + 'px'}; color: coral; Padding: 0.25 rem 1 rem; border: solid 2px coral; border-radius: 3px; Margin: 0.5 rem; &:hover { background-color: bisque; } `;
Copy the code

Styled – Components Creating a new component creates a componentId identifier for the component. The code is as follows:

counter++;
const componentId = 'sc-' + hash('sc' + counter);
Copy the code

For the Styled – Components component created first, the componentId is sc-bDVaja

In general, styled- Components creates a unique identifier using the MurmurHash algorithm and then converts the hash value to a string of out-of-order letters.

Once the identifier is created, styled- Components will insert the

<style data-styled-components>
  /* sc-component-id: sc-bdVaJa */
</style>
Copy the code

Once a new component is created, componentId and Target are stored as static properties on the button component:

StyledComponent.componentId = componentId;
StyledComponent.target = TargetComponent;
Copy the code

As you can see, simply creating a Property-Components component does not consume much performance. Even if you define hundreds of components without using them, you’ll end up with one or more annotated

Components created using the Styled factory function have an important aspect: They all inherit from a hidden BaseStyledComponents class, which implements some lifecycle methods. Let’s see.

componentWillMount()

Let’s create an instance of the Button component and mount it on the page:

ReactDOM.render(
  <Button sizeValue={24}>I'm a button</Button>.document.getElementById('root'));Copy the code

The BaseStyledComponent’s componentWillMount() life cycle is called, which releases some important signals:

  1. Parsing tag templates: This algorithm and our implementationmyStyledFactories are very similarButtonComponent instance:
<Button sizeValue={24}>I'm a button</Button>
Copy the code

We get the CSS style string as follows:

font-size: 24px;
color: coral;
padding: 0.25 rem 1rem;
border: solid 2px coral;
border-radius: 3px;
margin: 0.5 rem;
&:hover {
  background-color: bisque;
}
Copy the code
  1. Generates the CSS class name: Each component instance has a unique CSS class name, which is also based onMurmurHashThe algorithm,componentIdAs well asevaluatedStylesString generated:
const className = hash(componentId + evaluatedStyles);
Copy the code

So our Button instance generates a className of jsZVzX.

The class name is then saved to the component’s state, with a field named generatedClassName.

  1. Preprocessing CSS: We use stylis, a popular CSS preprocessor, to extract CSS strings:
const selector = '. ' + className;
const cssStr = stylis(selector, evaluatedStyles);
Copy the code

Here is the final CSS style for the Button instance:

.jsZVzX {
  font-size: 24px;
  color: coral;
  padding: 0.25 rem 1rem;
  border: solid 2px coral;
  border-radius: 3px;
  margin: 0.5 rem;
}
.jsZVzX:hover{
  background-color: bisque;
}
Copy the code
  1. Inject a CSS string into the page: You can now inject CSS into<style>Inside the tag, after comments with component identification:
<style data-styled-components>
  /* sc-component-id: sc-bdVaJa */
  .sc-bdVaJa {} .jsZVzX{font-size:24px;color:coral; . }.jsZVzX:hover{background-color:bisque; } </style>Copy the code

As you can see, styled- Components also inject componentId(.sc-bDVaja) into the page and do not define a style for.sc-bDVaja.

render()

Styled components only need to create the component’s className (className) after completing the CSS related work:

const TargetComponent = this.constructor.target; // In our case just 'button' string.
const componentId = this.constructor.componentId;
const generatedClassName = this.state.generatedClassName;

return (
  <TargetComponent
    {. this.props}
    className={this.props.className+ "' +componentId+ "' +generatedClassName} / >
);
Copy the code

Styled – Components adds three class names to the rendered element (TargetComponent) :

  1. this.props.classNameThe name of the class passed from the parent component is optional.
  2. componentId— A unique identifier for a component, but not a component instance. This class name has no CSS style, but when neededReferencing other componentsCan be used as a nested selector.
  3. generatedClassName— A unique prefix for a component with CSS style

Great! The final rendered HTML looks like this:

<button class="sc-bdVaJa jsZVzX">I'm a button</button>
Copy the code

componentWillReceiveProps()

Now let’s try to change the props of the Button component after it has been mounted. All you need to do is add an interactive event to the Button component:

let sizeValue = 24;

const updateButton = () = > {
  ReactDOM.render(
    <Button sizeValue={sizeValue} onClick={updateButton}>
      Font size is {sizeValue}px
    </Button>.document.getElementById('root')); sizeValue++; } updateButton();Copy the code

Online sample

You click a button, componentWillReceiveProps () is called, and sizeValue will since, after the process and componentWillMount () :

  1. Parsing tag templates
  2. Generates a new CSS class name
  3. Stylis preprocessing style
  4. Inject CSS into the page

Looking at the Browser developer tools after clicking the button multiple times, you can see:

<style data-styled-components>
  /* sc-component-id: sc-bdVaJa */
  .sc-bdVaJa {}
  .jsZVzX{font-size:24px;color:coral; . }.jsZVzX:hover{background-color:bisque; }.kkRXUB{font-size:25px;color:coral; . }.kkRXUB:hover{background-color:bisque; }.jvOYbh{font-size:26px;color:coral; . }.jvOYbh:hover{background-color:bisque; }.ljDvEV{font-size:27px;color:coral; . }.ljDvEV:hover{background-color:bisque; }</style>
Copy the code

Yes, all classes differ only with the font size attribute, and useless CSS classes have not been removed. Why is that? Because removing useless classes increases performance overhead, see this article.

Here is a small point of optimization: you can add a isStatic variable, in componentWillReceiveProps () check this variable, if the components do not need to insert style, skip, so as to avoid unnecessary calculation.

Performance tuning tips

Once you understand how the Styled – Components underlayer works, you can better focus on performance optimization.

This is an example of a button with Easter eggs (Hint: Click the button more than 200 times and you will see the Styled – Components hidden message in the console. No kidding! 😉).

Here are the hidden eggs:

The styled. Button Component generates more than 200 class names that require frequent style changes. Consider using the attrs method Example: const Component = style.div.attrs ({style: ({ background }) => ({ background, }), })`width: 100%; ` <Component />Copy the code

The refactored Button component looks like this:

const Button = styled.button.attrs({
  style: ({ sizeValue }) = > ({ fontSize: sizeValue + 'px'})})` color: coral; Padding: 0.25 rem 1 rem; border: solid 2px coral; border-radius: 3px; Margin: 0.5 rem; &:hover { background-color: bisque; } `;
Copy the code

However, not all dynamic styles should take this approach. My own rule is to use the style attribute for high fluctuation values. Such as:

  • likeword cloudThis can be highly customizablefont-sizeThe components of the
  • A list of labels with different colors retrieved from the server

However, if your buttons are diverse, such as Default, Primary, WARN, etc., it is better to use style strings.

In the example below, I use the Styled – Components package of the development version, while you should use the faster production version. In the React project, the Styled Components production package disables many of the development-environment warnings, which are important. It uses cssstylesheet.insertrule () to inject the generated styles into the page, Node.appendchild () is used in development (Evan Scott shows how fast insertRule really is here)

You may also consider using the babel-plugin-Styled – Components plug-in, which compresses and preprocesses style files.

conclusion

Styled – Components’ workflow is concise, it creates the necessary CSS styles before the component is rendered, and is fast enough if the tag strings need to be parsed and the CSS preprocessed.

This article does not cover all aspects of styled- Components, but I will try to focus on the main points.

For this article, THE Styled – Components version I use is V3.3.3. Its source code may change in future releases.