14 советов по написанию чистого React-кода. Часть 1

Автор оригинала: jsmanifest
  • Перевод
Написание чистого кода — это навык, который становится обязательным на определённом этапе карьеры программиста. Особенно этот навык важен тогда, когда программист пытается найти свою первую работу. Это, по существу, то, что делает разработчика командным игроком, и то, что способно либо «завалить» собеседование, либо помочь его успешно пройти. Работодатели, принимая кадровые решения, смотрят на код, написанный их потенциальными сотрудниками. Код, который пишет программист, должен быть понятен не только машинам, но и людям.



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

1. Деструктурируйте свойства


Деструктурирование свойств (в англоязычной терминологии React их называют «props») — это хороший способ сделать код чище и улучшить возможности по его поддержке. Дело в том, что это позволяет чётко выражать или объявлять то, что использует некая сущность (вроде компонента React). При этом такой подход не принуждает разработчиков вчитываться в реализацию компонента для того, чтобы выяснить состав свойств, связанных с ним.

Деструктурирование свойств, кроме того, даёт программисту возможность задавать их значения по умолчанию. Подобное встречается довольно-таки часто:

import React from 'react'
import Button from 'components/Button'

const MyComponent = ({ placeholder = '', style, ...otherProps }) => {
  return (
    <Button
      type="button"
      style={{
        border: `1px solid ${placeholder ? 'salmon' : '#333'}`,
        ...style,
      }}
      {...otherProps}
    >
      Click Me
    </Button>
  )
}

export default MyComponent

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

Например, у нас имеется функция authenticate, которая принимала в качестве параметра token, используемый для аутентификации пользователей. Позже понадобилось сделать так, чтобы она принимала бы сущность jwt_token. Эта необходимость была вызвана изменением структуры ответа сервера. Благодаря применению деструктурирования можно легко организовать поддержку обоих параметров и при этом не сталкиваться с необходимостью менять большую часть кода функции:

// до рефакторинга
async function authenticate({ user_id, token }) {
  try {
    const response = await axios.post('https://someapi.com/v1/auth/', {
      user_id,
      token,
    })
    console.log(response)
    return response.data
  } catch (error) {
    console.error(error)
    throw error
  }
}

// после рефакторинга
async function authenticate({ user_id, jwt_token, token = jwt_token }) {
  try {
    const response = await axios.post('https://someapi.com/v1/auth/', {
      user_id,
      token,
    })
    console.log(response)
    return response.data
  } catch (error) {
    console.error(error)
    throw error
  }
}

Сущность jwt_token будет оцениваться в тот момент, когда код дойдёт до token. В результате, если jwt_token окажется действительным токеном, и сущность token окажется равной undefined, в token попадёт значение jwt_token. Если же в token уже было какое-то значение, не являющееся по правилам JS ложным (то есть — некий реальный токен), то в token просто останется то, что там уже было.

2. Размещайте файлы компонентов в продуманной структуре папок


Взглянем на следующую структуру директорий:

  • src

    • components
    • Breadcrumb.js
    • CollapsedSeparator.js
  • Input

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • Card

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

В состав навигационных цепочек (breadcrumbs) могут входить разделители (separators). Компонент CollapsedSeparator импортируется в файле Breadcrumb.js. Это даёт нам знание о том, что в реализации рассматриваемого проекта они связаны. Однако тот, кто не владеет этой информацией, может предположить, что Breadcrumb и CollapsedSeparator — это пара совершенно самостоятельных компонентов, которые никак друг с другом не связаны. Особенно — если у CollapsedSeparator нет неких чётких признаков того, что этот компонент связан с компонентом Breadcrumb. Среди таких признаков, например, может быть префикс Breadcrumb, применяющийся в имени компонента, что может превратить имя в нечто вроде BreadcrumbCollapsedSeparator.js.

Так как мы знаем о том, что Breadcrumb и CollapsedSeparator связаны друг с другом, то мы, возможно, зададимся вопросом о том, почему они не помещены в отдельную папку, вроде Input и Card. При этом мы можем начать делать различные предположения о том, почему материалы проекта имеют именно такую структуру. Скажем, тут можно подумать о том, что эти компоненты поместили на верхний уровень проекта для того, чтобы, заботясь о тех, кто будет работать с проектом, помочь им быстро найти эти компоненты. В результате взаимоотношения частей проекта выглядят для нового разработчика довольно-таки туманно. Применение методик написания чистого кода должно давать прямо противоположный эффект. Речь идёт о том, что благодаря им новый разработчик получает возможность читать чужой код и мгновенно схватывать суть ситуации.

Если использовать в нашем примере хорошо продуманную структуру директорий, то получится примерно следующее:

  • src

    • components
  • Breadcrumb

    • index.js
    • Breadcrumb.js
    • CollapsedSeparator.js
  • Input

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • Card

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

Теперь уже неважно то, сколько будет создано компонентов, связанных с компонентом Breadcrumb. До тех пор, пока их файлы будут располагаться в той же директории, что и Breadcrumb.js, мы будем знать о том, что они связаны с компонентом Breadcrumb:

  • src

    • components
  • Breadcrumb

    • index.js
    • Breadcrumb.js
    • CollapsedSeparator.js
    • Expander.js
    • BreadcrumbText.js
    • BreadcrumbHotdog.js
    • BreadcrumbFishes.js
    • BreadcrumbLeftOvers.js
    • BreadcrumbHead.js
    • BreadcrumbAddict.js
    • BreadcrumbDragon0814.js
    • BreadcrumbContext.js
  • Input

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • Card

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

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

import React from 'react'
import Breadcrumb, {
  CollapsedSeparator,
  Expander,
  BreadcrumbText,
  BreadcrumbHotdog,
  BreadcrumbFishes,
  BreadcrumbLeftOvers,
  BreadcrumbHead,
  BreadcrumbAddict,
  BreadcrumbDragon0814,
} from '../../../../../../../../../../components/Breadcrumb'

const withBreadcrumbHotdog = (WrappedComponent) => (props) => (
  <WrappedComponent BreadcrumbHotdog={BreadcrumbHotdog} {...props} />
)

const WorldOfBreadcrumbs = ({
  BreadcrumbHotdog: BreadcrumbHotdogComponent,
}) => {
  const [hasFishes, setHasFishes] = React.useState(false)

  return (
    <BreadcrumbDragon0814
      hasFishes={hasFishes}
      render={(results) => (
        <BreadcrumbFishes>
          {({ breadcrumbFishes }) => (
            <BreadcrumbLeftOvers.Provider>
              <BreadcrumbHotdogComponent>
                <Expander>
                  <BreadcrumbText>
                    <BreadcrumbAddict>
                      <pre>
                        <code>{JSON.stringify(results, null, 2)}</code>
                      </pre>
                    </BreadcrumbAddict>
                  </BreadcrumbText>
                </Expander>
                {hasFishes
                  ? breadcrumbFishes.map((fish) => (
                      <>
                        {fish}
                        <CollapsedSeparator />
                      </>
                    ))
                  : null}
              </BreadcrumbHotdogComponent>
            </BreadcrumbLeftOvers.Provider>
          )}
        </BreadcrumbFishes>
      )}
    />
  )
}

export default withBreadcrumbHotdog(WorldOfBreadcrumbs)

3. Давайте компонентам имена, используя стандартные соглашения об именовании


Использование неких стандартов при именовании компонентов упрощает тому, кто не является автором проекта, чтение кода этого проекта.

Например, к именам компонентов высшего порядка (higher order component, HOC) обычно добавляют префикс with. К таким именам компонентов привыкли многие разработчики:

import React from 'react'
import hoistNonReactStatics from 'hoist-non-react-statics'
import getDisplayName from 'utils/getDisplayName'

const withFreeMoney = (WrappedComponent) => {
  class WithFreeMoney extends React.Component {
    giveFreeMoney() {
      return 50000
    }

    render() {
      return (
        <WrappedComponent
          additionalMoney={[
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
          ]}
          {...this.props}
        />
      )
    }
  }

  WithFreeMoney.displayName = `withFreeMoney(${getDisplayName(
    WrappedComponent,
  )}$)`
  hoistNonReactStatics(WithFreeMoney, WrappedComponent)

  return WithFreeMoney
}

export default withFreeMoney

Предположим, некто решит отступить от этой практики и сделать так:

import React from 'react'
import hoistNonReactStatics from 'hoist-non-react-statics'
import getDisplayName from 'utils/getDisplayName'

const useFreeMoney = (WrappedComponent) => {
  class WithFreeMoney extends React.Component {
    giveFreeMoney() {
      return 50000
    }

    render() {
      return (
        <WrappedComponent
          additionalMoney={[
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
            this.giveFreeMoney(),
          ]}
          {...this.props}
        />
      )
    }
  }

  WithFreeMoney.displayName = `useFreeMoney(${getDisplayName(
    WrappedComponent,
  )}$)`
  hoistNonReactStatics(WithFreeMoney, WrappedComponent)

  return WithFreeMoney
}

export default useFreeMoney

Это — совершенно работоспособный JavaScript-код. Имена тут составлены, с технической точки зрения, верно. Но префикс use принято использовать в других ситуациях, а именно — при именовании хуков React. В результате, если некто пишет программу, которую планируется показывать кому-то ещё, ему стоит внимательно относиться к именам сущностей. Особенно это актуально для тех случаев, когда кто-то просит посмотреть его код и помочь ему решить какую-то проблему. Дело в том, что тот, кто будет читать чужой код, вполне возможно, уже привык к определённой схеме именования сущностей.

Отступления от общепринятых стандартов затрудняют понимание чужого кода.

4. Избегайте «ловушки логических значений»


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

Предположим, мы создали компонент Typography, который может принимать следующие опции: 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'title', 'subheading'.

Что именно повлияет на выходные данные компонента в том случае, если опции передаются ему в следующем виде?

const App = () => (
  <Typography color="primary" align="center" subheading title>
    Welcome to my bio
  </Typography>
)

Те, у кого есть определённый опыт в работе с React (или, скорее, c JavaScript), уже могут предположить, что опция title перекроет опцию subheading из-за особенностей работы системы. Последняя опция перезапишет первую.

Но проблема тут заключается в том, что мы не можем, не глядя в код, точно сказать о том, до каких пределов будет применена опция title или опция subheading.

Например:

.title {
  font-size: 1.2rem;
  font-weight: 500;
  text-transform: uppercase;
}

.subheading {
  font-size: 1.1rem;
  font-weight: 400;
  text-transform: none !important;
}

Даже хотя title и выигрывает, CSS-правило text-transform: uppercase применяться не будет. Происходит это из-за более высокой специфичности правила text-transform: none !important, которое имеется в subheading. Если не проявлять в таких ситуациях осторожность — отладка подобных ошибок в стилях может стать чрезвычайно сложным занятием. Особенно — в тех случаях, когда код не выводит в консоль неких предупреждений или сообщений об ошибках. Это может усложнить сигнатуру компонента.

Вот один из возможных вариантов решения этой проблемы — применение более чистого варианта компонента Typography:

const App = () => <Typography variant="title">Welcome to my bio</Typography>

Вот код компонента Typography:

import React from 'react'
import cx from 'classnames'
import styles from './styles.css'

const Typography = ({
  children,
  color = '#333',
  align = 'left',
  variant,
  ...otherProps
}) => {
  return (
    <div
      className={cx({
        [styles.h1]: variant === 'h1',
        [styles.h2]: variant === 'h2',
        [styles.h3]: variant === 'h3',
        [styles.h4]: variant === 'h4',
        [styles.h5]: variant === 'h5',
        [styles.h6]: variant === 'h6',
        [styles.title]: variant === 'title',
        [styles.subheading]: variant === 'subheading',
      })}
    >
      {children}
    </div>
  )
}

Теперь, когда в компоненте App мы передаём компоненту Typography variant="title", мы можем быть уверены в том, что на вывод компонента повлияет только title. Это избавляет нас от необходимости анализа кода компонента, выполняемого для того, чтобы понять, как будет выглядеть то, что этот компонент выведет на экран.

Для работы со свойствами можно применить и простую конструкцию if/else:

let result
if (variant === 'h1') result = styles.h1
else if (variant === 'h2') result = styles.h2
else if (variant === 'h3') result = styles.h3
else if (variant === 'h4') result = styles.h4
else if (variant === 'h5') result = styles.h5
else if (variant === 'h6') result = styles.h6
else if (variant === 'title') result = styles.title
else if (variant === 'subheading') result = styles.subheading

Но главная сильная сторона подобного подхода заключается в том, что можно просто использовать следующую чистую однострочную конструкцию и поставить на этом точку:

const result = styles[variant]

5. Используйте стрелочные функции


Стрелочные функции представляют собой лаконичный и ясный механизм объявления функций в JavaScript (в данном случае правильнее будет говорить о преимуществе стрелочных функций над функциональными выражениями).

Однако в некоторых случаях разработчики не пользуются стрелочными функциями вместо функциональных выражений. Например — тогда, когда нужно организовать поднятие функций.

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

// Версия с объявлением функции
function Gallery({ title, images = [], ...otherProps }) {
  return (
    <CarouselContext.Provider>
      <Carousel>
        {images.map((src, index) => (
          <img align="center" src={src} key={`img_${index}`} />
        ))}
      </Carousel>
    </CarouselContext.Provider>
  )
}

// Версия со стрелочной функцией или с функциональным выражением
const Gallery = ({ title, images = [], ...otherProps }) => (
  <CarouselContext.Provider>
    <Carousel>
      {images.map((src, index) => (
        <img align="center" src={src} key={`img_${index}`} />
      ))}
    </Carousel>
  </CarouselContext.Provider>
)

Надо отметить, что, анализируя этот пример, сложно увидеть сильные стороны стрелочных функций. Их красота в полной мере проявляется тогда, когда речь идёт о простых однострочных конструкциях:

// Версия с объявлением функции
function GalleryPage(props) {
  return <Gallery {...props} />
}

// Версия со стрелочной функцией или с функциональным выражением
const GalleryPage = (props) => <Gallery {...props} />

Уверен, что подобные однострочные конструкции всем придутся по душе.

6. Размещайте независимые функции за пределами собственных хуков


Мне доводилось видеть то, как некоторые программисты объявляют функции внутри собственных хуков, но при этом данные хуки не особенно нуждаются в таких функциях. Подобное слегка «раздувает» код хуков и усложняет его чтение. Сложности в чтении кода возникают из-за того, что его читатели могут начать задаваться вопросами о том, действительно ли хук зависит от функции, которая находится у него внутри. Если это не так — лучше переместить функцию за пределы хука. Это даст читателю кода чёткое понимание того, от чего хук зависит, а от чего — нет.

Вот пример:

import React from 'react'

const initialState = {
  initiated: false,
  images: [],
}

const reducer = (state, action) => {
  switch (action.type) {
    case 'initiated':
      return { ...state, initiated: true }
    case 'set-images':
      return { ...state, images: action.images }
    default:
      return state
  }
}

const usePhotosList = ({ imagesList = [] }) => {
  const [state, dispatch] = React.useReducer(reducer, initialState)

  const removeFalseyImages = (images = []) =>
    images.reduce((acc, img) => (img ? [...acc, img] : acc), [])

  React.useEffect(() => {
    const images = removeFalseyImages(imagesList)
    dispatch({ type: 'initiated' })
    dispatch({ type: 'set-images', images })
  }, [])

  return {
    ...state,
  }
}

export default usePhotosList

Если проанализировать этот код — можно понять, что функции removeFalseyImages, на самом деле, необязательно присутствовать внутри хука.Она не взаимодействует с его состоянием, а значит — её вполне можно разместить за его пределами и без проблем вызывать из хука.

7. Будьте последовательны при написании кода


Последовательный подход к написанию кода — это то, что часто рекомендуют тем, кто программирует на JavaScript.

В случае с React стоит обратить внимание на последовательный подход к применению следующих конструкций:

  1. Команды импорта и экспорта.
  2. Именование компонентов, хуков, компонентов высшего порядка, классов.

Я, импортируя и экспортируя компоненты, иногда использую нечто подобное следующему:

import App from './App'

export { default as Breadcrumb } from './Breadcrumb'

export default App

Но мне нравится и такой синтаксис:

export { default } from './App'
export { default as Breadcrumb } from './Breadcrumb'

Что бы ни выбрал программист, ему стоит последовательно использовать это в каждом создаваемом им проекте. Это упрощает и работу этого программиста, и чтение его кода другими людьми.

Очень важно придерживаться и соглашений по именованию сущностей.

Например, если некто дал хуку имя useApp, важно, чтобы и имена других хуков строились бы по похожей схеме — с использованием префикса use. Например, имя ещё какого-нибудь хука при таком подходе может выглядеть как useController.

Если не придерживаться этого правила, то код некоего проекта, в итоге, может оказаться примерно таким:

// хук #1
const useApp = ({ data: dataProp = null }) => {
  const [data, setData] = React.useState(dataProp)

  React.useEffect(() => {
    setData(data)
  }, [])

  return {
    data,
  }
}

// хук #2
const basicController = ({ device: deviceProp }) => {
  const [device, setDevice] = React.useState(deviceProp)

  React.useEffect(() => {
    if (!device && deviceProp) {
      setDevice(deviceProp === 'mobile' ? 'mobile' : 'desktop')
    }
  }, [deviceProp])

  return {
    device,
  }
}

Вот как выглядит импорт этих хуков:

import React from 'react'
import useApp from './useApp'
import basicController from './basicController'

const App = () => {
  const app = useApp()
  const controller = basicController()

  return (
    <div>
      {controller.errors.map((errorMsg) => (
        <div>{errorMsg}</div>
      ))}
    </div>
  )
}

export default App

С первого взгляда совершенно неочевидно то, что basicController — это хук, такой же, как и useApp. Это принуждает разработчика к чтению кода реализации того, что он импортирует. Делается это только для того, чтобы понять, с чем именно разработчик имеет дело. Если же последовательно придерживаться одной и той же стратегии именования сущностей, то подобной ситуации не возникнет. Всё окажется понятным с первого взгляда:

const app = useApp()
const controller = useBasicController()

Продолжение следует…

Уважаемые читатели! Как вы подходите к именованию сущностей в своих React-проектах?

RUVDS.com
1 100,25
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Комментарии 27

    +1
    Верно, хватает тех, кто забывает работает в команде, и что кто-то еще кроме него будет работать с его кодом.
      0
      Я, за свою долгую карьеру, понял одно, нету глобального понятия «Чистый код», есть стандарты в фирме, которой ты работаешь, и именно их и нужно использовать, что бы код выглядел одинаково, а так же что бы каждый программист в данной фирме смог разобрать твой код.
        –5
        Чтобы прочитать остальные 7 пунктов, оформите подписку на сервера ruvds… Ой, что это я?
          –2
          Не знал, что у ruvds есть фанатики.
          +2
          MyComponent = ({ placeholder = '', style, <b>...otherProps </b>}) => {
          
          style={{
                  border: `1px solid ${placeholder ? 'salmon' : '#333'}`,
                  <b>...style</b>,
                }}
                {<b>...otherProps</b>}

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

          } from '../../../../../../../../../../components/Breadcrumb'
          render={(results) => (
                  <BreadcrumbFishes>
                    {({ breadcrumbFishes }) => (
                      <BreadcrumbLeftOvers.Provider>
                        <BreadcrumbHotdogComponent>
                          <Expander>
                            <BreadcrumbText>
                              <BreadcrumbAddict>

          весело тем кто будет разбираться в таком коде :)
            0
            ../../../../../../../../../../

            Это шутка?
              0
              Не знаю насколько это шутка в статье, но я лично встречал проекты с таким издевательством :)
                –1
                Увы, абсолютные пути в импортах — это еще большее издевательство, которое легко отказывается работать в самые непредсказуемые моменты (корень не там, один из инструментов считает, что корень не там, код перенесен, типы TS вдруг отпали, и всё в таком духе).

                Относительные пути работают абсолютно всегда, пока сами сорсы лежат вместе, и рефакторятся автоматически любой средненькой IDE.
                  –1
                  Минусующим на всякий случай скажу, что когда люди пишут инструменты для того, чтоб заменять абсолютные пути на относительные — это очень хороший признак того, что с ними не всё гладко.
              +1
              у меня тоже глаза чуть не вытекли от увиденого, особенно:

              ...
              <BreadcrumbAddict>
                 <pre>
                   <code>{JSON.stringify(results, null, 2)}</code>
                 </pre>
              </BreadcrumbAddict>
              


              Может лутше подготавливать все данные заранее?
              И вообще, Вы веть должны понимать — react (как и vue, svelte, etc.) лиш «рисовалка», и гораздо важнее уметь правильно разделять приложение на независимые слои, с минимальными «точками соприкосновения», про это уже писали, правда не в контексте «фронтэнда».
              +1
              Чистый код это хорошо, но что делать если уже по колено?

              Посоветуйте генератор диаграм или чего другое для визуализации связей компонентов Vue/React. Есть говнокод на Vue, с которым я разбираюсь. Так вот он изобилует нестандартной иерархией компонентов и глобальными data-назначениями без человеческого Vuex (Component.data.prop = value… из др. метода компонента).
              Гуляю по коду в Webstorm и Chrome отладчике, (!) черчу диаграммы в Dia. Может существует extension чтобы я прокликал интерфейс и мне сгенерило карту отношений? Как по быстрому разобраться чтобы переписать все это и больше не выдирать волосы на голове?
                0

                Если правильно понял то вам нужен этот пакет ️ https://github.com/pahen/madge


                Он умеет кушать на вход ваши модули а на выход рисовать картинку зависимостей, помечая кольцевые красным цветом

                  0
                  Спасибо, гляну. Вообще, ищу что-то специфическое вроде Flowmaker, но под фронтенд с поддержкой темплейтов и прочим сопутствующим леголендом
                0
                Написание чистого кода (...) становится обязательным на определённом этапе карьеры программиста. Особенно (...) когда программист пытается найти свою первую работу.

                Нууу, удачи, как говорится, всем студентам писать чистый код еще до своей первой работы… :)
                  0
                  let result
                  if (variant === 'h1') result = styles.h1
                  else if (variant === 'h2') result = styles.h2
                  else if (variant === 'h3') result = styles.h3
                  else if (variant === 'h4') result = styles.h4
                  else if (variant === 'h5') result = styles.h5
                  else if (variant === 'h6') result = styles.h6
                  else if (variant === 'title') result = styles.title
                  else if (variant === 'subheading') result = styles.subheading

                  O_o не делайте так никогда, это пример плохого кода. Используйте switch в подобных конструкциях.

                  
                  const getStyles = variant => {
                    switch (variant) {
                      case 'h1':
                        return styles.h1
                      case 'h2':
                        return styles.h2
                      default:
                        return styles.default
                     }
                  }
                  

                  const getStyles = variant => (variant in styles ? styles[variant] : null)


                  После подобного и ../../../../../../../../../../, невозможно воспринимать статью всерьёз. Webpack alias, автору для справки.
                    0
                    Webpack alias, автору для справки.

                    Вы только не забывайте, что про webpack alias будет знать только вебпак. А не, например, IDE.

                    const getStyles = variant => (variant in styles? styles[variant]: null)

                    А потом этот код попадёт под минификацию и станет тыквой. Switch — нормально во всех случаях, а вот этот вариант — далеко не во всех.
                      +1
                      А потом этот код попадёт под минификацию и станет тыквой.

                      Если ваша минификация калечит даже такой код, то первое что вам стоило бы сделать — поднастроить её или удалить к чертям. У всего есть свои пределы. Минификация не должна влиять на то, как вы пишете код. Ну за редким исключением вроде штук вида dead code elimination.

                        –3
                        ваша минификация

                        Вы из тех, кто кроме готовых сайтиков всей кучей никогда ничего не писал?
                        Проблема кривой минификации может быть совсем даже не у вас, вот в чём соль. А у людей, использующих ваш код. И не всегда это такие люди, которым можно сказать «проблема на вашей стороне».

                        Сделать всё как надо у вас — никогда не проблема, во фронтэндском CI настраивается примерно всё, а если вдруг не настраивается, то есть другие инструменты, где таки да. А вот сделать так, чтоб ваше творение жило и работало во всех сценариях использования, в том числе не ваших, и в том числе и не особо правильных — это чуточку другой разговор.
                          +2

                          Какой-то нелепый переход на личности (рекомендую так больше не делать здесь) и несвязный текст. Что вы хотели сказать? Поясните детальнее. Моя позиция простая: минификация кода (uglifyjs наверное в вашем случае) НЕ ДОЛЖНА ломать код. Т.е. не должна переименовывать литералы и не должна переименовывать ключи объектов. Если она так делает, то она сильно агрессивная и ломает работу тех продуктов где используется (пока разработчики не выловят сами все эти проблемные места). У вас есть по этому поводу возражения?


                          Т.е. я не против того чтобы минификатор менял длинные имена на короткие двубуквенные, но он должен делать это ТОЛЬКО и ТОЛЬКО тогда когда он гарантирует тот факт, что это ничего не поломает в коде. И никак иначе. Выгода в полтора процента размера бандла однозначно не окупиться мириадой багов, которые может вызвать такое поведение минификатора.

                            –1
                            Какой-то нелепый переход на личности

                            Да ровно такой же, каков и ваш.

                            Что вы хотели сказать? Поясните детальнее.

                            Поясняю по буквам: пайплайн сборки не всегда полностью у вас под контролем, иногда его часть на стороне.

                            Если она так делает, то она сильно агрессивная и ломает работу тех продуктов где используется (пока разработчики не выловят сами все эти проблемные места). У вас есть по этому поводу возражения?

                            У меня — нет. Возражения могут быть у тех, кому, например, вы поставляете вашу либу. А на ваши заявления о том, что они сами виноваты — они всегда могут ответить «нет, не мы», и не всегда у вас будет возможность продолжать эту линию аргументов.
                            Вы так не делали и у вас подобных случаев не возникало? Ну поздравляю.

                            Т.е. я не против того чтобы минификатор менял длинные имена на короткие двубуквенные, но он должен делать это ТОЛЬКО и ТОЛЬКО тогда когда он гарантирует тот факт, что это ничего не поломает в коде.

                            А я вам о том, что минификатор не всегда на вашей стороне. И можно написать так, что минификатор в принципе ничего не поломает (с какими угодно настройками), а можно написать так, что может быть и поломает. И разницу между тем и этим — не всегда нужно учитывать, но вообще неплохо бы хотя бы осознавать.
                              +1
                              И можно написать так, что минификатор в принципе ничего не поломает (с какими угодно настройками)

                              Нет, нельзя. Достаточно сократить до двух букв какой-нибудь window или document — и всё поломается как бы вы не писали код.

                                0
                                Окей, принимается.

                                Можно написать так, что минификатор JS-кода для известного контекста в принципе ничего не поломает (с какими угодно настройками).
                                  0

                                  Так ведь известный контекст тоже может быть настройкой!

                                +1
                                А я вам о том, что минификатор не всегда на вашей стороне.

                                Нет никаких объективных причин принимать в рассчёт такие случаи, когда на чужой стороне стоит неадекватно настроенный минификатор, который ломает даже такие тривиальные случаи.


                                Ну, ок, исключение — когда вы пишете B2B продукт под конкретную контору, где время остановилось, страшное легаси и жуткая бюрократия, поддержка IE8 & Silverlight. В этом случаем это наименьшая из ваших проблем да.


                                Вы так не делали и у вас подобных случаев не возникало?

                                Я вам даже больше скажу — и никогда не возникнет. Подобными извращениями ("перетюненный" uglifyjs) баловались люди года 4 назад. У них стали ломаться ангуляры и тонны других библиотек. Потом пришло осознание простой истины — сама затея пережимать ломая код идиотична по своей природе.


                                P.S. предлагаю на этом наш "холивар" закончить. Ваша точка зрения мне более чем понятна.

                                  –1
                                  Ну, ок, исключение — когда вы пишете B2B продукт под конкретную контору, где время остановилось, страшное легаси и жуткая бюрократия, поддержка IE8 & Silverlight. В этом случаем это наименьшая из ваших проблем да.

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

                                  P.S. предлагаю на этом наш «холивар» закончить.

                                  Надо же, людям пишешь «позвольте, но не всегда всё так просто, как вы считаете», а они это холиваром потом называют.
                        0

                        Почему бы не упростить вот так:


                        const result = styles[variant];
                        +1

                        У автора написано вот такое:


                        const removeFalseyImages = (images = []) =>
                        images.reduce((acc, img) => (img ? [...acc, img] : acc), [])

                        Но я но согласен! Такое нужно переписать вот так


                        const removeFalseyImages = (images = []) => images.filter(Boolean);

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое