featureslices.dev

v1.0

[![](https://img.shields.io/badge/feature/slices-1.0-blue)](https://featureslices.dev/v1.0)

Usage

In that examples TypeScript used as common language, but you free to choose.

Applications

Examples

Introduction

FeatureSlices define your frontend application structure as easy and convenient.

Convention

Structure

Source code of the application contained in src:

Api

This is reusable functions or classes to make requests to the server. It is can be REST-like, GraphQL, WebSocket, etc.

Code in the api can be partially generated (swagger or OpenAPI).

What is API:

src/
  api/
    api-name.ts
    request.ts

For example api-name is a group of requests, having a common purpose. More that one namespace can be created.

Not recommended to add src/api/index.ts file, because different api should be separated one from one. If you import api/user, you see just requests related to user. But if you import api, you can be drowned in tons of requests for application.

Example:

src/
  api/
    post.ts
    feed.ts
    session.ts
    request.ts

In this example: post, feed, session is a namespaced requests, but request is a base function, to make requests.

Example post.ts:

import { request } from './request'

interface PostNew {
  title: string;
  content: string;
}

export const postsList = () => request({ method: "GET", path: "/posts" })

export const postCreate = (post: PostNew) => request({ method: "POST", path: "/posts", body: post })

export const postGet = (postId: string) => request({ method: "GET", path: `/posts/${postId}` })

Structure and shape of each method on your own. But it should be consistent with all other requests.

Recommended to setup imports in your project, to allow import from root (src/) or use some aliases.

From api source code you can import only src/lib and package dependencies, nothing more.

Good:

import { postsList } from 'api/post'
// or
import * as posts from '@api/post'

Bad:

import { posts } from '../../../api'

Features

It is list of your reusable code and shared state, groupped by one idea or entity.

Structure:

src/
  features/
    feature-name/
      components/
      models/
      templates/
      __tests__/
      index.ts
      readme.md
      stories.tsx

Feature structure:

Lib

Common utils or helper code should be groupped by meaning and placed at its own library in this directory.

Example:

You have two functions: parse a string to a username, and parse an array of strings to list of usernames. Instead of placing a first function to a lib/string, and a second to a lib/array, group it by meaning: username. The best place: lib/username

Structure

src/
  lib/
    lib-name/
      index.ts
      readme.md
      tests.ts

Pages

What is page? Page is a component rendered on specific url. Or another router’s unit, that separates one component of another.

Structure:

src/
  pages/
    page-name/
      index.tsx
      model.ts
    another/
      nested/
        index.tsx
        model.ts
      index.tsx
      model.ts
    paths.ts
    routes.ts
    index.ts

Example component file:

// src/pages/register/confirm/index.ts
import * as React from 'react'
import { Just } from '@app/ui'

import * as model from './model'

export const RegisterConfirmPage: React.FC = () => {
  model.useLogic();

  return (
    <RegisterConfirmContent>
      Hello from registration confirmation page
    </RegisterConfirmContent>
  )
}

const RegisterConfirmContent: React.FC = ({ children }) => (
  <Just example>
    {children}
  </Just>
)

Example model file:

// src/pages/register/confirm/model.ts
import * as React from 'react'

export const useLogic = () => {
  React.useEffect(() => {
    console.log("Handle on mount here")
  }, [])
}

Example paths file:

// src/pages/paths.ts

export const paths = {
  home: () => `/`,
  login: () => `/login`,
  register: () => `/register`,
  registerConfig: (code: string) => `/register/confirm/${code}`,
}

Example routes file:

// src/pages/routes.ts
import { paths } from './paths'

import { HomePage } from './home'
import { LoginPage } from './login'
import { RegisterPage } from './register'
import { RegisterConfirmPage } from './register/confirm'
import { Error404Page } from './error404'

// react-router-config example
export const routes = [
  {
    path: paths.home(),
    exact: true,
    component: HomePage,
  },
  {
    path: paths.login(),
    exact: true,
    component: LoginPage,
  },
  {
    path: paths.register(),
    exact: true,
    component: RegisterPage,
  },
  {
    path: paths.registerConfig(':code'), // pass path parameter
    exact: true,
    component: RegisterConfirmPage,
  },
  {
    path: '*',
    component: Error404Page,
  },
]

Example pages index file:

// src/pages/index.ts
import { renderRoutes } from 'react-router-config';
import { routes } from './routes'

export const Pages = () => renderRoutes(routes)

Now Pages component can be easily used in Application to bootstrap routing.

Example application file:

// src/application.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import { GlobalTemplate } from '@app/ui'
import { Pages } from './pages'

const Application = () => (
  <GlobalTemplate>
    <Pages />
  </GlobalTemplate>
)

ReactDOM.render(
  <BrowserRouter>
    <Application />
  </BrowserRouter>,
  document.querySelector("#root"),
)

UI

TO DO