https://github.com/yornaath/use-saga-reducer
Use redux-saga with react reducer hook, with convenience methods for running sagas from components.
https://github.com/yornaath/use-saga-reducer
hooks react react-hooks redux-saga saga state-management
Last synced: 3 months ago
JSON representation
Use redux-saga with react reducer hook, with convenience methods for running sagas from components.
- Host: GitHub
- URL: https://github.com/yornaath/use-saga-reducer
- Owner: yornaath
- Created: 2020-02-18T16:00:50.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2023-01-07T15:00:54.000Z (almost 3 years ago)
- Last Synced: 2025-06-25T11:17:13.295Z (4 months ago)
- Topics: hooks, react, react-hooks, redux-saga, saga, state-management
- Language: TypeScript
- Homepage:
- Size: 1.87 MB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 18
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# use-saga-reducer
Use redux-saga with react reducer hook, with convenience methods for running sagas from components.
```typescript
import React, { useEffect, useState, useRef } from 'react'
import { useSaga } from '../../../src'
import { takeEvery, select, delay, put } from 'redux-saga/effects'const reducer = (count: number, action: any) => {
switch(action.type) {
case 'INCREMENT':
return count + 1
case 'DECREMENT':
return count - 1
case 'RESET_COUNT':
return 0
}
return count
}const resetAt = (max: number) => function*() {
yield takeEvery('INCREMENT', function*() {
const count = (yield select()) as number
if(count >= max) {
yield put({type: 'RESET_COUNT'})
}
})
}export const Counter = (props: {max: number}) => {
const [count, dispatch, useRun] = useSaga(reducer, 0, resetAt(props.max))
const [cycle, setCycle] = useState(0)useRun(function* () {
yield takeEvery('RESET_COUNT', function*(a) {
setCycle( cycle +1 )
})
}, [cycle])return (
dispatch({type: 'INCREMENT'})}>+
dispatch({type: 'DECREMENT'})}>-
counter: {count}
cycle: {cycle}
)
}
```### Example with typed store
#### SimpleComponent.tsx
```typescript
import React from 'react'
import { reducer, saga, ping, State, ActionEvent } from './store'
import { useSaga } from '../../../src'
import { take, select } from 'redux-saga/effects'const initialState: State = {
events: []
}const takePings = (until: number) => function* () {
while(yield take('ping')) {
const events = (yield select((s) => s.events)) as ActionEvent[]
const nrOfPings = events.filter(e => e === 'ping').lengthif(nrOfPings === until)
return "counting done"
}
}export default () => {
const [state, dispatch, useRun] = useSaga(
reducer,
initialState,
saga
)const counted = useRun(takePings(4), [])
return (
<>
dispatch(ping())}>Ping
{state.events.map((event, index) => (
{event} {' '}
))}
{
state.error &&
{ state.error }
}
{
counted.loading ?
counting.. :
counted.value ?
{ counted.value } :
""
}
>
)
}
```#### GlobalSagaReducer.tsx
```typescript
import React from 'react'
import { useSaga } from 'use-saga-reducer'
import { reducer, saga, ping, State } from './store'const initialState: State = {
events: []
}const sagaContext = createSagaContext(reducer, initialState, saga)
export default () => {
return (
)
}export const Inner = () => {
const [state, dispatch, useRun] = sagaContext.use()
return (
<>
dispatch(ping())}>Ping
{state.events.map((event, index) => (
{event} {' '}
))}
{
state.error &&
{ state.error }
}>
)
}
```#### store.ts
```typescriptimport { take, put, delay, select, SelectEffect } from 'redux-saga/effects'
export type Ping = 'ping'
export type Pong = 'pong'export type PingActionType = {
type: Ping
}export const ping = (): PingActionType => ({
type: 'ping'
})export type PongActionType = {
type: Pong
}export const pong = (): PongActionType => ({
type: 'pong'
})export type ActionType =
PingActionType
| PongActionTypeexport type ActionEvent =
Ping
| Pongexport type State = {
events: ActionEvent[]
error?: string
}export const reducer = (state: State, action: ActionType): State => {
switch(action.type) {case 'ping':
const isWaitingForPong = state.events[state.events.length - 1] === 'ping'if(isWaitingForPong)
return { ...state, error: 'Invariant: trying to ping while ponging!'}return {
events: [...state.events, 'ping']
}case 'pong':
const isWaitingForPing = state.events[state.events.length - 1] === 'pong'if(isWaitingForPing)
return { ...state, error: 'Invariant: trying to pong while pinging!'}return {
events: [...state.events, 'pong']
}}
}export const saga = function* saga() {
while(yield take('ping')) {
yield delay(800)
yield put(pong())
}
}
```