GrabDuck

Организация компонентов в проекте

:

Многие руководствуются рекомендациями Presentational and Container Components, но уважаемый автор признаётся в сносках, что концепция разделения спорная, и компоненты можно смешивать. А если это так, то зачем тащить чемодан без ручки? Все компоненты проекта удобнее хранить в одной общей папке. Какие плюсы:
  • Простота навигации по файловой системе.
  • Уникальные имена компонентов проекта.
  • Импорт без боли ('../../../../../..').

Когда проект вырастет, следует дробить его на приватные npm-пакеты, инкапсулируя реализацию. Но не выращивать дерево подпапок внутри папки компонентов — развивать и поддерживать такое ощутимо сложнее. Проверено.

Все компоненты организованы по доменному принципу в одной папке src/componens. Например, можно определить домены: Post — отображение публикации, PostForm — форма редактирования публикации.

Как создать доменный компонент — src/components/Post/Post.js.

Почему нельзя использовать index.js вместо Post.js — соблюдается уникальность имен файлов компонентов внутри проекта, так упрощается навигация по вкладкам редактора (и в WebStorm будет работать ценная функция "Find Usages" для контекстного меню выделенного файла — см. примечание).

Все компоненты в папке доменного компонента получают префикс доменного компонента (Post*.js).

Подпапки в папке доменного компонента не допускаются, внутри плоская структура файлов из компонентов-потомков (PostTitle.js, PostBody.js) и компонентов-предков (PostViewPage.js, PostListPage.js). Компоненты-потомки используются внутри доменного компонента, а компоненты-предки используются снаружи (в роутере, например).

Для импорта доменных компонентов следует задать внутри каждой папки доменного компонента свой package.json, в котором прописать точку входа "main":

{
  "name": "Post",
  "version": "0.0.0",
  "private": true,
  "main": "./Post.js",
}

Кроме того, внутри файла доменного компонента (Post.js) объявлен реэкспорт компонентов-предков:

import PostViewPage from './PostViewPage'
import PostListPage from './PostListPage'
//...
export { PostViewPage, PostListPage }
export default Post

К сожалению, нельзя использовать конструкцию "export from" (ограничение WebStorm), например:

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

В результате не требуется явного доступа к файлам внутри папки компонентов, например:

import Post from 'components/Post/Post'

По соглашению, разрешается использовать импорт только по имени доменного компонента:

import Post, { PostViewPage, PostListPage } from 'components/Post'

Исходники

Примечание по функции "Find Usages" в WebStorm. Как минимум, есть три контекста использования: 1) по выделенному файлу, 2) по выделенной переменной или символу, 3) по выделенному default в экспорте компонента.

1) Выделим файл components/Post/PostViewPage.js, результат поиска:

// Post.js
import PostViewPage from './PostViewPage'

2) Выделим символ PostViewPage внутри файла PostViewPage.js, результат поиска:

// PostViewPage.js
PostViewPage.propTypes = {}

3) Выделим default в экспорте компонента PostViewPage, результат поиска:

// routes.js
import { PostViewPage } from 'components/Post'
<Route exact path="/post/:id(\d+)/" component={PostViewPage} />

Как видно, третий способ использования выдает наиболее полезную информацию для компонента-предка.


PS


CSS внутри компонентов

Найден правильный способ прикрутить Styled-JSX для Create React App. Теперь CSS-блоки живут внутри файлов компонентов естественным для себя образом — в CSS-формате (против инлайн-стилей JS-объектов). И не нужно беспокоиться за глобальную область видимости.


Абсолютные пути в импорте

Работают относительно папки src, пример:

import MyComponent from 'components/MyComponent'

Конфигурация babel-plugin-module-resolver
['module-resolver', { 'root': ['src'] }]

Настройка WebStorm

Для папки src в контекстом меню выполнить: Mark Directory as > Resource Root.


Настройка Atom

  • Установить плагин js-hyperclick.
  • Для доменных компонентов прописывать в package.json путь до src:
    "moduleRoots": ["../.."]

Настройка VSCode

Нужно добавить jsconfig.json в корень проекта:

{
  "compilerOptions": {
    "target": "ES6"
  },
  "exclude": [
    "node_modules"
  ]
}