# Features

All application logic is defined within a **Feature**. Combing a [**React context** ](https://reactjs.org/docs/context.html)with **react-states** allows us to drive both UI and side effects with only dispatching and consuming explicit state.

![](/files/-Mj_SstdKICutIEbzE0h)

### Consuming a feature

```typescript
import { match } from 'react-states'
import { useAuth } from '../features/AuthFeature'

const Auth = () => {
  const [auth, dispatch] = useAuth()
  
  /*
    The feature allows you to match on the current
    states. This matching is exhaustive, meaning you have to define
    what you want to return in each state.
  */
  return match(auth, {
    UNAUTHENTICATED: () => (
      <button onClick={() => {
        /*
          The only way to drive the application forward is to
          dispatch. The feature decides if this intent
          to move the application forward is valid, which again
          triggers any necessary side effects for that new state
        */
        dispatch({ type: 'SIGN_IN' })
      }}>Sign In</button>
    ),
    AUTHENTICATING: () => (
      <div>Authenticating...</div>
    ),
    AUTHENTICATED: ({ user }) => (
      <h1>Hello ${user.name}</h1>
    ),
    ERROR: ({ error }) => (
      <h4>Ops, something bad happened: {error}</h4>
    )
  })
}
```

{% hint style="warning" %}
**Best practices**

* [ ] When consuming the state of the feature, make use of the **match** helper to ensure that any values produced based on a state is covered for all states
  {% endhint %}

### Implementing a Feature

The main file is called **Feature.tsx**. The naming conventions within a feature is generic, meaning every type and variable points to "feature" and not the domain the feature represents. This keeps feature implementations as similar as possible, you only parse the implementation details and not the generic types and variables. You will rather use the **index.ts** file to expose named exports.

{% code title="features/Auth/index.ts" %}

```typescript
export {
  FeatureProvider as AuthProvider,
  useFeature as useAuth
} from './Feature'

export type {
  User,
  PublicFeature as Auth
} from './Feature'
```

{% endcode %}

{% hint style="warning" %}
**Best practices**

* [ ] The component and hook is to be exposed with its name
* [ ] Export the public feature as the name of the feature
* [ ] Any types you want to be consumable from the feature should also be exported
  {% endhint %}

{% code title="features/Auth/Feature.tsx" %}

```typescript
import React, { createContext, useContext } from 'react'
import {
  createReducer,
  useStateEffect,
  useCommandEffect,
  useSubscription,
  States,
  StatesTransition
} from 'react-states'
import { ApiAction } from '../environment/api'

/*
  Types specifically related to the feature is exported from
  the feature
*/
export type User = { name: string }

export type State =
  | {
    state: 'SIGNED_OUT'
  }
  | {
    state: 'SIGNED_IN'
    user: User
  }
  | {
    state: 'SIGNING_OUT'
  }
  | {
    state: 'SIGNING_IN'
  }
  | {
    state: 'ERROR'
    error: string
  }
  
/*
  Dispatches that should only happen within the feature is
  defined with its own type
*/
export type PrivateAction = {
  type: 'ERROR_TIMEOUT'
}

/*
  Dispatches that can be used by consuming components
  is defined in its own type prefixed by Public
*/
export type PublicAction = {
  type: 'SIGN_IN'
}

/*
  Commands that can be run related to state transitions
*/
export type Command = {
  type: 'RELOAD_APP'
}

/*
  The tuple returned from useReducer, typed with
  only public actions
*/
export type PublicFeature = States<State, PublicAction>

/*
  The tuple returned from useReducer
*/
export type Feature = States<State, PrivateAction | PublicAction | ApiAction, Command>

/*
  Exact return type for transition handlers in the reducer
*/
export type Transition = StateTransition<Feature>


/* 
  Generate a context for the public version of the feature
*/ 
const featureContext = createContext({} as PublicFeature)

/*
  We also define a hook to consume the feature
*/
export const useFeature = () => useContext(reducerContext)

/*
  We create the reducer which expresses state transitions and guards
  these transitions by only allowing certain actions in certain states.
*/
const reducer = createReducer<Feature>({
  SIGNING_IN: {
    /*
      This feature is subscribing to the Api and has access to deal
      with any of its actions. The first argument is the current state,
      "SIGNING_IN" in this case. The second argument is the action
    */
    'API:AUTHENTICATED': (state, { user }): Transition => ({
      state: 'SIGNED_IN',
      user
    }),
    /*
      We can return a new state, optionally using data from
      the action and/or existing state, or just keep the existing
      state
    */
    'API:UNAUTHENTICATED': (): Transition => ({ state: 'SIGNED_OUT' }),
    /*
      Currently you have to set an explicit return type to ensure
      full type safety of the transition, but TypeScript will
      have a new Exact utility that removes this requirement
    */
    'API:SIGN_IN_ERROR': ({ error }): Transition => ({ state: 'ERROR', error })
  },
  SIGNED_IN: {},
  SIGNING_OUT: {
    /*
      When we want to run effects with relying on changing the explicit state,
      we can return a tuple where the second item is a command
    */
    'API:UNAUTHENTICATED': (state): Transition => [
      state,
      {
        cmd: 'RELOAD_APP'
      }
    ],
    'API:SIGN_OUT_ERROR': ({ error }): Transition => ({ state: 'ERROR', error })
  },
  SIGNED_OUT: {
    SIGN_IN: (): Transition => ({ state: 'SIGNING_IN' })
  },
  ERROR: {}
})

interface Props {
  children: React.ReactNode
  initialState?: State
}

const FeatureProvider = ({
  children,
  /*
    We set a default initial state, which can be changed
    during testing
  */
  initialState = { state: 'SIGNING_IN' }
}: Props) => {
  /*
    The react-states architecture separates your application from
    the environment it is running in
  */
  const { api, env } = useEnvironment()
  
  /* 
    We initialize our reducer with the initial state
  */ 
  const feature = useReducer(reducer, initialState)
  
  /*
    We destructure for conveniance inside the feature
  */
  const [state, dispatch] = feature
  
  /*
    We funnel any events from the API into our reducer
  */
  useSubscription(api.subscription, dispatch)
  
  /*
    The react-states architecture only allows components to dispatch
    actions to the feature which transitions the state. That means
    side effects can only trigger as a reaction to a state transitions
  */
  useStateEffect(state, 'SIGNING_IN', () => api.signIn())
  
  /*
    We can also trigger side effects related to commands
  */
  useCommandEffect(state, 'RELOAD_APP', () => env.reload())
  
  /*
    We expose the state and dispatch on the context of the provider. It is important
    that we reference the value returned from "useReducer" as this ensures
    optimal reconciliation
  */
  return (
    <featureContext.Provider value={feature}>
      {children}
    </featureContext.Provider>
}
```

{% endcode %}

{% hint style="warning" %}
**Best practices**

* [ ] Any private actions to the feature is to be prefixed with **Private**
* [ ] Any public actions to the feature is to be prefixed with **Public**
* [ ] The public feature is typed with the public actions
* [ ] The feature itself is typed with all actions and optionally commands
* [ ] All types used within the feature should be exported from the feature, even if it is a strict re-export from an other source
* [ ] Define the reducer outside of the feature component using the **createReducer** factory
  {% endhint %}

### Creating data types

Sometimes features consumes data from the environment. Typically the types returned from an environment interface is exactly what you want to expose in the state of your feature, but not always. **No matter all the types that a feature consumes should also be exported from the feature**. That means:

```typescript
import { SandboxDTO } from './environment/api'

// Sometimes we just reuse a type from the environment
export type Sandbox = SandboxDTO

// Other times we explicitly define the type, where 
// some environment effect might give us way more data,
// but we do not care about it
export type User = { username: string }

const SandboxFeature = () => {}

```

This pattern ensures that whenever a type is inferred from a feature, you will always find it on the feature itself. This means you can discover all the types a feature defines by looking at its exports.

{% hint style="warning" %}
**Best practices**

* [ ] Avoid re exporting types from the environment where you do not intend to consume all the data. Rather export a custom type with a subset of the types from the environment
  {% endhint %}

### Exploding features

Certain features might have many transition effects and/or states. In that case it can be an idea to split the feature into parts.

```bash
features/
  AFeature/
    index.ts
    Feature.test.tsx
    Feature.tsx
    reducer.ts
    effects.ts
    types.ts
```

The **index** file exports a named version of the provider and types.

{% code title="features/FeatureA/index.ts" %}

```typescript
export {
  FeatureProvider as AProvider,
  useFeature as useA
} from './Feature'

export type {
  PublicFeature as A
} from './Feature'
```

{% endcode %}

The **Feature** file should only contain the definition of the provider component itself.

{% code title="features/featureA/Feature.tsx" %}

```typescript
import React, { createContext } from 'react'
import { reducer } from  './reducer'
import { State, PublicFeature } from './types'
import { useApi, useSignInEffect } from './effects'

export * from './types'

const featureContext = createContext({} as PublicFeature)

interface Props {
  children: React.ReactNode
  initialState?: State
}

export const Feature = ({
  children,
  initialState = { state: 'SIGNED_OUT' }
}: Props) => {
  const feature = useReducer(reducer, initialState)
  
  useApi(feature)
  useSignInEffect(feature)
  
  return (
    <featureContext.Provider value={feature}>
      {children}
    </featureContext.Provider>
  )
}
```

{% endcode %}

All types are defined in its own file.

{% code title="features/featureA/types.ts" %}

```typescript
import { States } from 'react-states'
import { ApiAction } from '../environment/api'

export type User = { name: string }

export type State =
  | {
    state: 'SIGNED_IN'
    user: User
  }
  | {
    state: 'SIGNED_OUT'
  }
  | {
    state: 'SIGNING_IN'
  }
  | {
    state: 'SIGNING_OUT'
  }
  | {
    state: 'ERROR'
    error: string
  }

export type PrivateAction = {
  type: 'ERROR_TIMEOUT'
}

export type PublicAction = {
  type: 'SIGN_IN'
}

export type Command = {
  cmd: 'RELOAD_APP'
}

export type Transition = StateTransition<State, Command>

export type PublicFeature = States<State, PublicAction>

export type PrivateFeature = States<State, PrivateAction | PublicAction | ApiAction, Command>
```

{% endcode %}

The reducer file holds the definition of transitions in the reducer.

{% code title="features/featureA/reducer.ts" %}

```typescript
import { createReducer } from 'react-states'
import { Transition, Feature } from './types'

export const reducer = createReducer<Feature>({
  SIGNING_IN: {
    'API:AUTHENTICATED': (_, { user }): Transition => ({ state: 'SIGNED_IN', user }),
    'API:UNAUTHENTICATED': (): Transition => ({ state: 'SIGNED_OUT' }),
    'API:SIGN_IN_ERROR': (_, { error }): Transition => ({ state: 'ERROR', error })
  },
  SIGNED_IN: {},
  SIGNED_OUT: {
    SIGN_IN: (): Transition => ({ state: 'SIGNING_IN' })
  },
  SIGNING_OUT: {
    'API:UNAUTHENTICATED': (state): Transition => [state, { cmd: 'RELOAD_APP' }],
    'API:SIGN_OUT_ERROR': ({ error }): Transition => ({ state: 'ERROR', error })
  },
  ERROR: {}
})
```

{% endcode %}

The effects file can have multiple effect definitions. Splitting the states between the effects, even having multiple effects on the same state. Organize them in related groups.

{% code title="features/featureA/effects.ts" %}

```typescript
import { useStateEffect, useSubscription } from 'react-states'
import { useEnvironment } from '../environment'
import { Feature } from './types' 
  
export const useApi = ([, dispatch]: Feature) => {
  const { api } = useEnvironment()
  
  useSubscription(api.subscription, dispatch)
}

export const useSignInEffect = ([state]: Feature) => {
  const { api } = useEnvironment()
  
  useStateEffect(state, 'SIGNING_IN', () => api.signIn())
}
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://react-states.csb.dev/features.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
