Часть 2 - статически типизированные стилизованные компоненты

Если вас заинтересовала первая часть статьи, нажмите на ссылку ниже 🤓



Теперь, когда мы создали наш первый компонент, вы, вероятно, заметили, что мы не использовали ни статический машинописный текст, ни стили условных стилей компонентов с помощью свойств. Чтобы условно изменить стили компонента, мы можем воспользоваться синтаксисом строки шаблона внутри определения компонента.

// src/components/button/index.tsx
import styled from "styled-components";

const StyledButton = styled.button`
  padding-bottom: 10px;
  padding-left: 20px;
  padding-right: 20px;
  padding-top: 15px;
  background-color: ${props => (props.active ? "red" : "green")};
  &:active {
    outline: 0;
    color: #fff;
    top: 1px;
  }
`;
export const Button = StyledButton;

Но похоже, что с нашим кодом что-то не так. Мы не указали, какие свойства принимает наш компонент. Это вызовет красную подсветку IDE и ошибку компиляции. Чтобы исправить это, нам нужно создать интерфейс и использовать его как общий тип свойств нашего компонента.

// src/components/button/index.tsx
import styled from "styled-components";
interface ButtonProps {
  active?: boolean;
}
const StyledButton = styled.button<ButtonProps>`
  padding-bottom: 10px;
  padding-left: 20px;
  padding-right: 20px;
  padding-top: 15px;
  background-color: ${props => (props.active ? "red" : "green")};
(...)

Ошибок нет 🎉. Чтобы передать свойства стилизованному компоненту, мы просто передаем их, как и любой другой компонент реакции.

// src/components/button/index.tsx
import React from "react";

import { storiesOf } from "@storybook/react";
import {Button} from './';

storiesOf("Button", module)
  .add("with text", () => (
    <div>
      <Button active={true}>Active button</Button>
      <Button active={false}>Not active button</Button>
    </div>
  ));

Теперь вроде все работает нормально. История показывает правильные стили для статических свойств. Давайте создадим интерактивную версию этой истории. Чтобы предоставить историям временное состояние, мы можем использовать аддон-сборник рассказов @ sambego / storybook-state.

npm i @sambego/storybook-state

Вот отрывок истории, которая меняет состояние кнопки после каждого нажатия:

// src/components/button/index.tsx
import React from "react";

import { storiesOf } from "@storybook/react";
import { Button } from "./";
import { State, Store } from "@sambego/storybook-state";

const store = new Store({
  active: false
});

storiesOf("Button", module).add("with text", () => (
  <State store={store}>
    {state => (
      <Button
        active={state.active}
        onClick={() => {
          store.set({ active: !state.active });
        }}
      >
        Active button
      </Button>
    )}
  </State>
));

Тематика

Тематизация - еще одна замечательная особенность библиотеки стилизованных компонентов. Он позволяет хранить общие значения, такие как цвета, отступы или даже функции, в глобальном объекте, который будет предоставлен всем компонентам. Он становится еще более мощным, когда мы объединяем эту функцию с хорошими качествами машинописного текста.

Чтобы добавить поддержку темы, оберните свой компонент в ThemeProvider, чтобы предоставить вашему компоненту дополнительное свойство под названием тема.

// src/components/button/button.stories.js
import { ThemeProvider } from "styled-components";

...

<ThemeProvider theme={{activeColor: 'red', regularColor: 'green'}}>
    <Button
        active={state.active}
        onClick={() => {
          store.set({ active: !state.active });
        }} 
    >
          Active button
    </Button>
</ThemeProvider>

...

Это дает нам возможность ссылаться на атрибуты темы в самом стилизованном компоненте.

const StyledButton = styled.button<ButtonProps>`
 ...
  background-color: ${props => (props.active ? props.theme.activeColor : props.theme.regularColor)};
...

Свойство props темы предоставляется самими стилизованными компонентами, и нам не нужно было указывать его структуру, потому что его тип установлен на любой (что в основном полностью исключает проверку типов машинописного текста).

При работе с более крупными проектами вам, вероятно, не захочется гадать, что и что значит. Это даже пригодится, когда вы пытаетесь прочитать код своей 6-месячной давности, поверьте мне 🙈. С красивым статически типизированным кодом нет необходимости гадать, поскольку все является результатом чего-то другого. Вот почему нам не нужно НИ ОДИН в нашем коде !!! Как это сделать? Нам нужно указать машинописному тексту, какую тему мы используем. Для этого давайте определим тип нашей темы, как указано ниже. И реэкспорт в стиле «стилизованных компонентов» со статически типизированной темой.

// src/theme.ts
export interface Theme {
    activeColor: string;
    regularColor: string;
}
// src/styled.ts
import * as baseStyled from "styled-components";
import { Theme } from "./theme"; //Import our interface
const {
  default: styledTyped /* Rename default export of baseStyled */,
  ThemeProvider
} = baseStyled as baseStyled.ThemedStyledComponentsModule<Theme>;
// And cast it to ThemedStyledComponentsModule<Theme> with previously specified Theme type
export { ThemeProvider };
export default styledTyped;

Затем мы должны изменить стилизованный экземпляр в нашем компоненте кнопки, чтобы использовать эту повторно экспортированную строго типизированную версию стилизованных компонентов, созданных выше.

// src/components/button/index.ts
import styled from "../../styled"; //Changed to our local styled

interface ButtonProps {
  active?: boolean;
}
const StyledButton = styled.button<ButtonProps>`
...

Если это не убеждает вас в том, что машинописный текст - это круто, у меня есть несколько других примеров React в процессе разработки. Я уверен, что это изменит способ написания наших приложений и поможет избежать множества ошибок без необходимости проверять код вручную.

В следующем рассказе я расскажу о тестировании, сборке и публикации вашего пакета в репозитории npm. Мы также поэкспериментируем с другими дополнениями для сборников рассказов 🤩