https://github.com/drmats/red-g
Statically typed actions and reducers for redux.
https://github.com/drmats/red-g
actions javascript reducers redux typescript
Last synced: 2 months ago
JSON representation
Statically typed actions and reducers for redux.
- Host: GitHub
- URL: https://github.com/drmats/red-g
- Owner: drmats
- License: apache-2.0
- Created: 2021-10-09T06:27:08.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2024-03-01T15:42:17.000Z (over 2 years ago)
- Last Synced: 2024-09-23T01:54:05.802Z (almost 2 years ago)
- Topics: actions, javascript, reducers, redux, typescript
- Language: TypeScript
- Homepage: https://drmats.github.io/red-g/
- Size: 1.4 MB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# red-g
Statically typed actions and reducers for **[redux](https://redux.js.org/)**.
[](https://www.npmjs.com/package/red-g)
[](https://github.com/drmats/red-g)
[](https://github.com/drmats/red-g)
[](https://www.npmjs.com/package/red-g)
```bash
$ npm i red-g
```
## ok, what's in here and how to use it?
1. actions as non-const enums (example `app/action_type.ts` file):
```typescript
export enum AppActionType {
RESET = "App/RESET",
READY = "App/READY",
NOT_READY = "App/NOT_READY",
DELAYED = "App/DELAYED",
CLEAR_ERROR = "App/CLEAR_ERROR",
}
```
2. easy action creators - define only those carrying some payload, empty
ones are defined automatically (example `app/action.ts` file):
```typescript
import { actionCreators } from "red-g";
import { AppActionType } from "./action_type";
export default actionCreators(AppActionType, {
READY: (error?: string) => ({ error }),
DELAYED: (condition: boolean) => ({ condition }),
});
```
3. slice of state (`app/state.ts`):
```typescript
export default {
// usual stuff
ready: false,
delayed: false,
// last application error - example use
// of "type predicate" matcher
// (matching actions by payload content)
error: null as string | null,
// how many actions of this type
// has been spawned? - somewhat artificial
// example of "boolean" matcher usage
// (matching actions using string
// operations on their type)
actionCount: 0,
};
```
4. fantastic slice reducers (example `app/reducer.ts` file) with matchers:
```typescript
import type { Action } from "red-g";
import {
isWithPayload,
sliceReducer,
} from "red-g";
import initState from "./state";
import app from "./action";
export default sliceReducer(initState) (
(slice) => slice
.handle(app.RESET, () => initState)
// this action can carry `error` payload
// but we're not interested in it (for now)
.handle(app.READY, (state) => ({
...state, ready: true,
})
// this action is not carrying any payload
// and was automatically defined by
// call to `actionCreators()`
.handle(app.NOT_READY, (state) => ({
...state, ready: false,
})
// this action is carrying simple payload and we can
// destructure it immediately
.handle(app.DELAYED, (state, { condition }) => ({
...state, delayed: condition,
})
// same situation as with `app.NOT_READY`
// (shown here for completness of the example)
.handle(app.CLEAR_ERROR, (state) => ({
...state, error: null,
})
// match all actions that carry payload with
// `error` key ("type predicate" matcher)
.match(
(action): action is Action<{ error: string }> =>
isWithPayload(action) && action.payload.error,
(state, { error }) => ({
...state, error,
}),
)
// match all actions whose `type` field starts with
// `App/` prefix ("boolean" matcher)
.match(
(action) => action.type.startsWith("App/"),
(state) => ({
...state, actionCount: state.actionCount + 1,
}),
),
);
```
5. now, you can combine all of your slice reducers (and actions and thunks...)
into nice trees (as you do usually) - example `state_logic.ts` file:
```typescript
import appAction from "./app/action";
import appReducer from "./app/reducer";
export const action = {
app: appAction,
// ... and others
};
export const reducer = {
app: appReducer,
// ... and others
};
```
6. slice reducers are compatible with regular redux flow:
```typescript
import {
createStore,
combineReducers,
} from "redux";
import {
bindActionCreatorsTree,
} from "red-g";
import {
action,
reducer,
} from "./state_logic";
const rootReducer = combineReducers(reducer);
// redux store creation
const store = createStore(
rootReducer,
// ... and other usual stuff
);
// bound actions tree
const act = bindActionCreatorsTree(
action, store.dispatch,
);
```
There's more to be happy about if you use
[IntelliSense](https://code.visualstudio.com/docs/editor/intellisense).
## can I use [immer](https://immerjs.github.io/immer/)?
Sure! [Curried producers](https://immerjs.github.io/immer/curried-produce)
are supported out of the box:
```typescript
import type { Action } from "red-g";
import {
isWithPayload,
sliceReducer,
} from "red-g";
import produce from "immer";
import initState from "./state";
import app from "./action";
export default sliceReducer(initState) (
(slice) => slice
.handle(app.RESET, () => initState)
.handle(app.READY, produce((draft) => {
draft.ready = true;
})
.handle(app.DELAYED, produce((draft, { condition }) => {
draft.delayed = condition;
})
.match(
(action): action is Action<{ error: string }> =>
isWithPayload(action) && action.payload.error,
produce((draft, payload) => {
draft.error = payload.error;
}),
),
);
```
## documentation
> [API Reference](https://drmats.github.io/red-g/)
## namespace
### **static** redux
```javascript
redux
```
> ```javascript
> { actionCreators: [Function: actionCreators],
> bindActionCreator: [Function: bindActionCreator],
> bindActionCreators: [Function: bindActionCreators],
> bindActionCreatorsTree: [Function: bindActionCreatorsTree],
> createReducer: [Function: createReducer],
> defineActionCreator: [Function: defineActionCreator],
> emptyActionCreators: [Function: emptyActionCreators],
> isWithPayload: [Function: isWithPayload],
> isWithTypeField: [Function: isWithTypeField],
> payloadActionCreators: [Function: payloadActionCreators],
> sliceReducer: [Function: sliceReducer] }
> ```
## notes
Go ahead and [file an issue](https://github.com/drmats/red-g/issues/new)
or [submit a fresh PR](https://github.com/drmats/red-g/pulls)
if you found a bug 🐞.
## support
You can support this project via [stellar][stellar] network:
* Payment address: [xcmats*keybase.io][xcmatspayment]
* Stellar account ID: [`GBYUN4PMACWBJ2CXVX2KID3WQOONPKZX2UL4J6ODMIRFCYOB3Z3C44UZ`][addressproof]
## license
**red-g** is released under the Apache License, Version 2.0. See the
[LICENSE](https://github.com/drmats/red-g/blob/master/LICENSE)
for more details.
[stellar]: https://learn.stellar.org
[xcmatspayment]: https://keybase.io/xcmats
[addressproof]: https://keybase.io/xcmats/sigchain#d0999a36b501c4818c15cf813f5a53da5bfe437875d92262be8d285bbb67614e22