1.3.4 • Published 3 years ago

typedstate v1.3.4

Weekly downloads
1
License
MIT
Repository
github
Last release
3 years ago

TypedState

A React/Redux wrapper to reduce boilerplate and increase type safety and productivity

Why?

After using React with Redux, I found that using connect, creating reducers, and sagas required a lot of boilerplate.
In order to get the type safety I wanted, I would regularly have to modify old code to do so.

TypedState is an attempt to reduce the amount of effort introducing React and Redux into a project and to make it easier to use without being burdened by passing types around.

Project Goals

  • Provide a very low barrier for entry to React+Redux
  • Provide complete type safety in components, reducers, and sagas
  • Reduce boilerplate in React/Redux projects
  • Maintain a small and discoverable API to ensure high developer experience

Benefits

Reducers, sagas, and components gain a huge amount of type safety and type inference.
Action types, state

Example Setup

You will still need to add React and Redux dependencies!

  • react
  • redux
  • react-redux
  • react-dom

See the example folder for a more complete example project.

API

createStore()

Returns setup and saga functions

import { createStore } from 'typedstate'

function createStore(name: string, reducers: { [key: string]: Reducer })

createReducer()

Returns handle and reducer functions.
The reducer must be exported and passed to the createStore function.

import { createReducer } from 'typedstate'

function createReducer<State, Action>(initState: State, handler?: HandlerBody)

setup()

Returns the Redux store and the withState and withDispatch hooks.
Think of withState as a wrapper of the react-redux connect function.

See examples/App.tsx for several examples.

Example

See the example/ folder for a more complete example

Creating a Store

The amount of code required to create the store is small and less complicated.
You no longer need to maintain a union type of all of your actions nor an interface of your application state.
Simply adding a reducer will provide all of the type safety you need when using store and withState().

import { createStore } from 'typedstate'
import * as user from './user'
import * as game from './game'

const { setup, saga } = createStore('my app', {
  user: user.reducer,
  game: game.reducer,
})

const { store, withDispatch, withState } = setup()

export { saga, store, withDispatch, withState }

Creating a Reducer

Creating reducers and sagas is fast, easy, and declarative.

import { createReducer } from 'typedstate'

export { reducer }

interface State {
  state: 'init' | 'loading' | 'loaded'
  name?: string
  loggedIn: boolean
  error?: string
}

type Action =
  | { type: 'USER_REQUEST_LOGIN'; username: string; password: string }
  | { type: 'USER_RECEIVE_LOGIN'; name?: string; error?: string }
  | { type: 'USER_REQUEST_LOGOUT' }

const { reducer, handle } = createReducer<State, Action>({ state: 'init', loggedIn: false })

handle('USER_REQUEST_LOGIN', { error: undefined, name: undefined, state: 'loading' })
handle('USER_RECEIVE_LOGIN', (_state, action) => {
  return {
    state: 'loaded',
    name: action.name,
    error: action.error,
    loggedIn: action.error === undefined,
  }
})

handle('USER_REQUEST_LOGOUT', { name: undefined, error: undefined, loggedIn: false })

Alternative way of Creating a Reducer

import { createReducer } from 'typedstate'

interface State {
  state: 'init' | 'loading' | 'loaded'
  name?: string
  loggedIn: boolean
  error?: string
}

type Action =
  | { type: 'USER_REQUEST_LOGIN'; username: string; password: string }
  | { type: 'USER_RECEIVE_LOGIN'; name?: string; error?: string }
  | { type: 'USER_REQUEST_LOGOUT' }

export const { reducer } = createReducer<State, Action>(
  { state: 'init', loggedIn: false },
  {
    USER_REQUEST_LOGIN: () => ({ error: undefined, name: undefined, state: 'loading' }),
    USER_RECEIVE_LOGIN: (_, action) => {
      return {
        state: 'loaded',
        name: action.name,
        error: action.error,
        loggedIn: action.error === undefined,
      }
    },
    USER_REQUEST_LOGOUT: () => ({ name: undefined, error: undefined, loggedIn: false }),
  }
)

Creating a Component

Creating components takes less code

import * as React from 'react'
import { withState } from '/store'

export const Profile = withState(
  ({ user }) => user,
  ({ state, name, error, loggedIn, dispatch }) => {
    const login = () => dispatch({ type: 'USER_REQUEST_LOGIN', username, password })
    const logout = () => dispatch({ type: 'USER_REQUEST_LOGOUT' })
    const [username, setUsername] = React.useState('')
    const [password, setPassword] = React.useState('')

    return <div>Create your component as you normally would</div>
  }
)
1.3.4

3 years ago

1.3.3

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.2.2

3 years ago

1.2.0

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.3.0-beta2

3 years ago

1.3.0-beta1

3 years ago

1.3.0-beta3

3 years ago

1.3.0

3 years ago

1.2.1

3 years ago

1.1.2

3 years ago

1.0.0

4 years ago

0.3.0

4 years ago

0.2.5

4 years ago

0.2.3

4 years ago

0.2.4

4 years ago

0.2.2

4 years ago

0.2.0

4 years ago

0.1.1

4 years ago

0.1.0

4 years ago