/src
/app
- Страницы/Лейтауты и прочий стафф next.js
/public
-
/icons
- Иконки для svgr
/img
- Фотки/video
- Видео/components
-
/ИмяКомпонента
index.tsx
- Файл компонентаpresentation.tsx
- Необязательный Файл презентации (верстки), если он требуетсяcontainer.tsx
- Необязательный Файл контейнера (логика), если он требуетсяstyle/.modules/.scss
- Необязательный Файл стилей, если такой требуется. Исключительный ситуации, компановкой и мерджем стилей занимается cva/actions
- Серверный actions для next
, выносить сюда по такой логике:
/hooks
- Папка кастомных хуков для проекта/context
- Папка для контекстов react
, в ней же стоит делать Provider
, и маленькие useХуки
, которые используют контекст, если логика более сложная, то useХуки
, стоит выносить /hooks
/data
- Файлы с общими данными в них лучше хранить, ссылки в футере, телефоны и прочее/types
- .d.ts файлы проекта, глобальные типы В НИХ НЕ МОЖЕТ БЫТЬ ИМПРОТА ТИПОВ
global.d.ts
- Файл общеге редеклера/styles
- Общие стили проекта, то есть сюда будут помещены стили которые используются глобальное по проекту
_typography.scss
- Настройка типографии по проекту, классы (классы с размерами текста, шириной отступов и т.д) Импортирован глобально через addictionalData/mixins
- Миксины scss для упрощения стилей Необязательный
_mixins.scss
- Точка обьядинения. В него импротированы все миксины для использования Необязательный/functions
- Функции scss Необязательный
_functions.scss
- Точка обьядинения. В него импортированы все функции для использования НеобязательныйЭто мелкие библиотеки, помогающие решить руттинные вопрос
От такого вида компонента стоит отталкиваться
// Типизация пропсов
interface Props {}
// Стили
const styles = cva();
// Сам код компонента
export default function Component({}: Props) {}
Представим такую ситуацию, нам нужно сделать некие компонент Test
и зависимый TestText
, и мы не можем создать стрелочный FC
компонент внутри файла компонента Test
. Что делать в такой ситуации? Обратиться к соглашению о именовании
.
Test
файлы компонента
TestText
файлы компонента
То есть такой взаимосвязанные компонент, будет именоваться так НазваниеКорневогоКомпонентаНазваниеВзаимосвязанногоКомпонента
.
Когда могут наступить подобные ситуации?
Test
и нам нужно создать клиентский компонент внутри него, тогда мы обратимся к соглашению
и создадим TestText
как зависимый компонент и сделаем его клиентским@description
опишите этот случай, чтобы не происходило путаницыТак как весь react
построен фактически на функциях, то функциональные паттерны стоит использовать вместе с ним. К примеру паттерн HOC, фактически является паттерном Функция высшего порядка/HOF. Поэтому думаю применение паттерна частичное применение является неплохим способом организации кода. Сам паттерн крайне простой, перейдем к примерам
Представим у нас есть некий компонент Header
и Button
, на нужно создать ButtonHeader
, а в есть варианты Button
- primary
, secondary
, dark
.
Вариант dark
нам подходит, применяется от аналогичным пропсом, код будет выглядеть так
import Button from "@components/Button";
// Частичное применение. Мы зарезервировали конкретные параметры/пропсы
const ButtonHeader: FC = () => <Button dark className="styles" />
export default function Header() {
return (
<header>
<ButtonHeader />
</header>
)
}
Так в проекте используется cva для компановки стилей, то компоненты стоит разбивать когда того требует ситуация.
Разбиение на FC
К примеру, нам нужно вывести подкомпонент (То есть компонент локальный и связанный с тем где мы создаем).
// Стили относятся к HeaderLogo
const styleHeaderLogo = cva()
const HeaderLogo: FC = () => <div className={stylesLogo()}></div>
// Стили относятся к Header
const styles = cva();
export default function Header() {
return (
<header className={styles()}>
<HeaderLogo />
</header>
)
}
Вариантное разбиение
К примеру в нашей ситуации, нам не нужно создавать отдельный FC
для подкомпонента, но скомпоновать стили требуется тогда мы используем возможности cva variants
const styles = cva("base styles", {
variants: {
intent: {
headerLogo: "styles-header-logo"
},
},
});
export default function Header() {
return <header className={styles()}>
<div className={styles({ intent: 'headerLogo' })}></div>
</header>
}
ПРИМЕРЫ БАЗОВЫЕ ЧТОБЫ ОТ НИХ ОТТАЛКИВАТЬСЯ
Сложным будет назван такой компонент в котором и верстка и логика крайне обьемные и умещая их в один файл, получается огромное неконтролируемое количество кода. В таком случае стоит использовать паттерн Container/Presentational
паттерну, разделяя компонент на 2 файла -
presentation.tsx
- Верстка и небольшие части логики (к примеру условная отрисовка в зависимости от пропа) export default function Item({ someBooleanProp = false, someArrayProp = [] }) {
return (
<div>
{someBooleanProp && "true"}
{someArrayProp.map((item, i) => (
<span key={i}>{item}</span>
))}
</div>
);
}
То есть, небольшая условная отрисовка тут есть, перебор туда же, но сложной логики с помощью useState, useEffect
и прочего нет
container.tsx
- Логика компонента, он принимает в себя presentation
и работает с логикой прокидывая данные ему в пропсы, такой компонент зачастую клиентский
. import Item from "./presentation.tsx"
export default ItemContainer() {
const [someData, setSomeData] = useState([]);
useEffect(() => {
// Сложная логика обработки при монтировании
}, [])
return (
<Item someArrayProp={someData} someBooleanProp={someData.length > 0}></Item>
)
}
То есть, наша логика стала разбита и находится в отдельном файле, что повышает читабельность и расширяемость ресурса
index.ts
- Компонент для экспортаimport Item from "./container.tsx";
export default Item;
Бывают ситуации в next
когда нужно передать начальные данные из серверного компонента в клиентский в таком случае, не стоит создавать presentation.tsx
, а обойтись только container.tsx
+ ИмяКомпонента.tsx
и использовать container.tsx
в серверном окружении
Выносить маленькие компоненты при помощи этого паттерна просто глупо, стоит аккуратно использовать этот паттер, чтобы не усложнять структуру проекта. Использовать только если у компонента планируется сложная логика, или в клиентский компонент нужно передать данные из серверного
dev
npm i
npm run generate:env-types # Генерация типов env для typescript
npm run dev
production
npm i
npm run generate:env-types # Генерация типов env для typescript
npm run build
npm run start
Orval - Генератор типов из schema.yml/json от openApi. Позволяет быстро генерировать библиотеку типов и функций запроса к апи.
Библиотека используется только для генерации типов получаемых с swagger
.
Так как, Orval в проекте применяется как генератор типов для typescript
, а библиотека запросов самописная, такая ситуация сломает типы только для нее,
но если такая потребность все же создалась (к примеру были добавлены новые компоненты)
npm run regenerate:api-types # Регенерация апи типов
СМЕНА ТИПОВ У МОДЕЛЕЙ МАЛОВЕРОЯТНА В КОНЦЕ РАЗРАБОТКИ БЕКЭНДА, ПОТОМУ ЧТО МОДЕЛИ БАЗЫ НЕ ЧАСТО НУЖНО МЕНЯТЬ
В СЛУЧАЕ ЕСЛИ РЕГЕНЕРАЦИЯ НЕОБХОДИМА СТОИТ УБЕДИТЬСЯ В ТОМ ЧТО СТАРЫЕ УЖЕ СГЕНЕРИРОВАННЫЕ ТИПЫ НЕ УДАЛЕНЫ
Типы api
не стоит выносить за пределы actions
т.к они могут быть регенирированы, в таком случае регенерация будет затрагивать только actions
Если требуется посмотреть доку по типам, то
Сначала сгенерируйте доку
npm run genereate:doc-types
После запустите
npm run start:doc-types
manual
-
cp .env.example .env # Заполните .env
npm i
npm run build
npm run start
docker
-
cp .env.example .env.docker # Заполните .env специфичный для Docker, не забывая о standalone
npm i # Необязательно, чтобы ускорить билд докера и сгенирировать lock файл
docker-compose up -d # Старт docker-compose'a