Skip to content

Commit 12a836f

Browse files
committed
Updates typescript docs
1 parent 845d877 commit 12a836f

1 file changed

Lines changed: 171 additions & 1 deletion

File tree

README.md

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,177 @@ See [https://github.com/zalmoxisus/remote-redux-devtools#parameters](https://git
506506
507507
## Usage with Typescript
508508
509-
Easy Peasy has full support for Typescript. Detailed documentation is coming soon, however you can view the [original PR](https://github.com/ctrlplusb/easy-peasy/pull/57) for an example on how to use Typescript effectively.
509+
Easy Peasy has full support for Typescript, via its bundled definitions. More detailed documentation is coming soon, however, you can expand the below to get a general overview of how to use Typescript with Easy Peasy.
510+
511+
<details>
512+
<summary>Overview of using Typescript with Easy Peasy</summary>
513+
<p>
514+
515+
Firstly, you need to define a type that represents your model. Easy Peasy exports numerous types to help you declare your model correctly.
516+
517+
```typescript
518+
519+
import { Action, Reducer, Thunk, Select } from 'easy-peasy'
520+
521+
interface TodosModel {
522+
items: Array<string>
523+
// represents a "select"
524+
firstItem: Select<TodosModel, string | void>
525+
// represents an "action"
526+
addTodo: Action<TodosModel, string>
527+
}
528+
529+
interface UserModel {
530+
token?: string
531+
loggedIn: Action<UserModel, string>
532+
// represents a "thunk"
533+
login: Effect<UserModel, { username: string; password: string }>
534+
}
535+
536+
interface Model {
537+
todos: TodosModel
538+
user: UserModel
539+
// represents a custom reducer
540+
counter: Reducer<number>
541+
}
542+
```
543+
544+
Then you create your store.
545+
546+
```typescript
547+
// Note that as we pass the Model into the `createStore` function. This allows
548+
// full type checking along with auto complete to take place
549+
// 👇
550+
const store = createStore<Model>({
551+
todos: {
552+
items: [],
553+
firstItem: select(state =>
554+
state.items.length > 0 ? state.items[0] : undefined,
555+
),
556+
addTodo: (state, payload) => {
557+
state.items.push(payload)
558+
},
559+
},
560+
user: {
561+
token: undefined,
562+
loggedIn: (state, payload) => {
563+
state.token = payload
564+
},
565+
login: effect(async (dispatch, payload) => {
566+
const response = await fetch('/login', {
567+
method: 'POST',
568+
body: JSON.stringify(payload),
569+
headers: {
570+
'Content-Type': 'application/json',
571+
},
572+
})
573+
const { token } = await response.json()
574+
dispatch.user.loggedIn(token)
575+
}),
576+
},
577+
counter: reducer((state = 0, action) => {
578+
switch (action.type) {
579+
case 'COUNTER_INCREMENT':
580+
return state + 1
581+
default:
582+
return state
583+
}
584+
}),
585+
})
586+
```
587+
588+
You can use the store's standard APIs. They will be typed.
589+
590+
```typescript
591+
console.log(store.getState().todos.firstItem)
592+
593+
store.dispatch({ type: 'COUNTER_INCREMENT' })
594+
595+
store.dispatch.todos.addTodo('Install typescript')
596+
```
597+
598+
You can type your hooks too.
599+
600+
``` typescript
601+
import { useStore, useActions, Actions, State } from 'easy-peasy';
602+
import { Model } from './your-store';
603+
604+
function MyComponent() {
605+
const token = useStore((state: State<Model>) =>
606+
state.user.token
607+
)
608+
609+
const login = useActions((actions: Actions<Model>) =>
610+
actions.user.login,
611+
)
612+
613+
return (
614+
<button onClick={() => login({ username: 'foo', password: 'bar' })}>
615+
{token || 'Log in'}
616+
</button>
617+
)
618+
}
619+
```
620+
621+
The above can become a bit cumbersome - having to constantly provide your types to the hooks. To avoid this you can use @formula349's [tip](https://github.com/ctrlplusb/easy-peasy/issues/21#issuecomment-457644655), where you export pre-typed versions of the hooks along with your store.
622+
623+
```typescript
624+
// your-store.js
625+
626+
import {
627+
useStore as useStoreOriginal,
628+
useActions as useActionsOriginal,
629+
createStore
630+
} from 'easy-peasy'
631+
632+
export type Model = { ... }
633+
634+
export const store = createStore<Model>({ ... })
635+
636+
export function useStore<Result>(
637+
mapState: (state: State<Model>) => Result,
638+
externals?: Array<any>,
639+
) {
640+
return useStoreOriginal(mapState, externals);
641+
}
642+
643+
export function useActions<Result>(
644+
mapActions: (actions: Actions<Model>) => Result,
645+
) {
646+
return useActionsOriginal(mapAction);
647+
}
648+
```
649+
650+
We could then revise our previous example.
651+
652+
``` typescript
653+
import { useStore, useActions } from './your-store';
654+
655+
function MyComponent() {
656+
const token = useStore((state) => state.user.token)
657+
const login = useActions((actions) => actions.user.login)
658+
return (
659+
<button onClick={() => login({ username: 'foo', password: 'bar' })}>
660+
{token || 'Log in'}
661+
</button>
662+
)
663+
}
664+
```
665+
666+
We also support typing react-redux.
667+
668+
```typescript
669+
const Counter: React.SFC<{ counter: number }> = ({ counter }) => (
670+
<div>{counter}</div>
671+
)
672+
673+
connect((state: State<Model>) => ({
674+
counter: state.counter,
675+
}))(Counter)
676+
```
677+
678+
</p>
679+
</details>
510680
511681
<p>&nbsp;</p>
512682

0 commit comments

Comments
 (0)