react#Reducer TypeScript Examples

The following examples show how to use react#Reducer. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
function combineReducers<S, A>(
  reducers: ReducersMapObject<S, A>
): Reducer<S, A> {
  return ((state, action) =>
    Object.fromEntries(
      Object.entries<Reducer<any, A>>(reducers).map(([key, value]) => [
        key,
        value(state[key as keyof S], action),
      ])
    )) as Reducer<S, A>;
}
Example #2
Source File: useBigDecimalInputs.ts    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
reducer: Reducer<State, Action> = ({ values, serializedAddresses }, action) => ({
  serializedAddresses,
  values: {
    ...values,
    [action.payload.address]:
      action.type === Actions.SetFormValue
        ? setFormValue(values[action.payload.address], action)
        : setAmount(values[action.payload.address], action),
  },
})
Example #3
Source File: NotificationsProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
reducer: Reducer<State, Action> = (state, action) => {
  switch (action.type) {
    case Actions.Add: {
      const notification = action.payload
      const existing = state.some(n => n.id === notification.id)
      return existing ? state : [notification, ...state]
    }

    case Actions.MarkAsRead: {
      const id = action.payload
      return state.map(n => (n.id === id ? { ...n, read: true } : n))
    }

    case Actions.HideToast: {
      const id = action.payload
      return state.map(n => (n.id === id ? { ...n, hideToast: true } : n))
    }

    default:
      return state
  }
}
Example #4
Source File: ENSProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
reducer: Reducer<State, Action> = (state, action) => {
  switch (action.type) {
    case ActionType.Error:
      return { ...state, [action.payload.ensName]: { ...state[action.payload.ensName], error: action.payload.error } }
    case ActionType.Value:
      return { ...state, [action.payload.ensName]: { value: action.payload.address } }
    case ActionType.Fetch:
      return { ...state, [action.payload.ensName]: { ...state[action.payload.ensName], fetching: true } }
    default:
      return state
  }
}
Example #5
Source File: Metrics.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
reducer: Reducer<State<any>, Action<any>> = (state, action) => {
  switch (action.type) {
    case Actions.SetDateRange: {
      const dateRange = action.payload
      return {
        ...state,
        dates: state.dates.map(d => (d.dateRange === dateRange ? { ...d, enabled: true } : { ...d, enabled: false })),
      }
    }

    case Actions.ToggleType: {
      const type = action.payload
      return {
        ...state,
        metrics: state.metrics?.map(t => (t.type === type ? { ...t, enabled: !t.enabled } : t)),
      }
    }

    default:
      return state
  }
}
Example #6
Source File: UserDialsContext.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
userDialPreferencesReducer: Reducer<
  { current: UserDialPreferences; changes: UserDialPreferences; touched: boolean },
  UserDialsPreferencesAction
> = (state, action) => {
  switch (action.type) {
    case 'RESET':
      return { ...state, changes: state.current, touched: false }
    case 'SET_DIAL': {
      const changes = { ...state.changes, [action.payload.dialId]: action.payload.value }
      if (Object.keys(changes).length > 16) return { ...state }
      const touched = !!Object.keys(changes).filter(k => (state.current[k] ?? 0) !== changes[k]).length
      return { ...state, changes, touched }
    }
    case 'CURRENT':
      return { ...state, current: action.payload, changes: state.touched ? state.changes : action.payload }
    default:
      return state
  }
}
Example #7
Source File: index.tsx    From multisig-react with MIT License 6 votes vote down vote up
useNewLimitModal = (initialStep: Step): NewLimitModalHook => {
  // globally stored tokens
  const tokens = useSelector(extendedSafeTokensSelector)

  // setup the reducer with initial values
  const [state, dispatch] = useReducer<Reducer<State, Action>, State>(
    newLimitModalReducer,
    {
      step: initialStep,
      txToken: makeToken(),
      values: {},
    },
    (state) => state,
  )

  // define actions
  const create = useCallback<ActionCallback>((newState) => dispatch({ type: CREATE, newState, tokens }), [tokens])
  const review = useCallback<ActionCallback>((newState) => dispatch({ type: REVIEW, newState, tokens }), [tokens])

  // returns state and dispatch
  return [state, { create, review }]
}
Example #8
Source File: profile.tsx    From yasd with MIT License 6 votes vote down vote up
profileReducer: Reducer<IProfileContext, ReducerAction> = (
  state,
  action,
) => {
  switch (action.type) {
    case 'update':
      setServer(action.payload.host, action.payload.port, action.payload.key, {
        tls: action.payload.tls,
      })
      return {
        profile: action.payload,
      }
    case 'clear':
      return {
        profile: undefined,
      }
    case 'updatePlatformVersion': {
      if (!state.profile) {
        throw new Error(
          'updatePlatformVersion cannot be dispatched if the profile is absent.',
        )
      }

      const profile = state.profile
      const updated = {
        ...profile,
        platformVersion: action.payload.platformVersion,
      }

      updateStoredProfile(updated.id, updated)

      return {
        profile: updated,
      }
    }
    default:
      throw new Error()
  }
}
Example #9
Source File: reducer.ts    From antibody-web with MIT License 6 votes vote down vote up
appReducer: Reducer<AppState, AppAction> = (state, action): AppState => {
  switch (action.type) {
    case "SET_LOCALE":
      return {
        ...state,
        locale: action.locale
      };
    case "SET_ERROR":
      return { 
        ...state,
        error: action.error || undefined
      };
    case "LOGIN_SUCCESS":
      return {
        ...state,
        token: action.token
      };
    case "LOGOUT":
      return initialState;
  }
}
Example #10
Source File: reducer.ts    From antibody-web with MIT License 6 votes vote down vote up
testReducer: Reducer<TestState, TestAction> = (state, action): TestState => {
  switch (action.type) {
    case "SAVE_TEST":
      return {
        ...state,
        testRecord: action.testRecord
      };
  }
}
Example #11
Source File: originalFunction.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
originalFunction: Reducer<
  DebuggerStateOriginalFunction,
  DebuggerAction
> = (state, action) => {
  switch (action.type) {
    case "initFunction":
      return action.originalFunction;
    default:
      return state;
  }
}
Example #12
Source File: modifiedFunction.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
modifiedFunction: Reducer<
  DebuggerStateModifiedFunction,
  DebuggerAction
> = (state, action) => {
  switch (action.type) {
    case "initFunction":
      return pick(action.originalFunction, ["source", "typescript"]);
    case "updateSource":
      return {
        ...state,
        source: action.source,
        modified: true,
      };
    case "updateTypescript":
      return {
        ...state,
        typescript: action.typescript,
        modified: true,
      };
    default:
      return state;
  }
}
Example #13
Source File: functions.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
functions: Reducer<
  DebuggerStateOriginalFunction[],
  DebuggerAction
> = (state, action) => {
  switch (action.type) {
    case "initFunction":
      return action.functions;
    default:
      return state;
  }
}
Example #14
Source File: debugOutput.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
debugOutput: Reducer<DebuggerStateDebugOutput, DebuggerAction> = (
  state,
  action
) => {
  switch (action.type) {
    case "initFunction":
    case "updateDebugInput":
    case "updateSource":
    case "updateTypescript":
      return null;
    case "debugReturn":
      return action.output;
    default:
      return state;
  }
}
Example #15
Source File: debugInput.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
debugInput: Reducer<DebuggerStateDebugInput, DebuggerAction> = (
  state,
  action
) => {
  switch (action.type) {
    case "initFunction":
      return state?.functionName === action.originalFunction.name
        ? {
            ...state,
            userInput: false,
          }
        : {
            functionName: action.originalFunction.name,
            raw: "[\n  \n]",
            ok: true,
          };
    case "updateDebugInput": {
      return {
        ...state,
        ...processSerializableValue(action.input),
        userInput: true,
      };
    }
    default:
      return state;
  }
}
Example #16
Source File: coverage.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
coverage: Reducer<ProcessedCoverage, DebuggerAction> = (
  state,
  action
) => {
  switch (action.type) {
    case "initFunction":
    case "updateSource":
    case "updateTypescript":
    case "addTest":
    case "deleteTest":
      return;
    case "updateCoverage":
      return getProcessedCoverage(action.coverage);
    default:
      return state;
  }
}
Example #17
Source File: activeTab.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
activeTab: Reducer<DebuggerStateActiveTab, DebuggerAction> = (
  state,
  action
) => {
  switch (action.type) {
    case "initFunction":
      return {
        value: "function",
        group: "function",
      };
    case "switchTab": {
      const [group, index] = action.tab.split(":");
      return {
        value: action.tab,
        group,
        index: index && Number(index),
      };
    }
    case "saveDebugAsTest":
    case "addTest":
      return {
        value: `test:${action.nextTestIndex}`,
        group: "test",
        index: action.nextTestIndex,
      };
    case "deleteTest":
      return {
        value: "function",
        group: "function",
      };
    default:
      return state;
  }
}
Example #18
Source File: HustlerProvider.tsx    From dope-monorepo with GNU General Public License v3.0 6 votes vote down vote up
HustlerProvider = ({ 
  children, 
  initialHustlerData
}: HustlerProviderProps) => {
  const [hustler, dispatchHustler] = useReducer<Reducer<HustlerState, HustlerActions>>(
    HustlerReducer,
    initialHustlerData || INITIAL_STATE,
  );
  return (
    <HustlerContext.Provider value={hustler}>
      <HustlerDispatchContext.Provider value={dispatchHustler}>
        {children}
      </HustlerDispatchContext.Provider>
    </HustlerContext.Provider>
  );
}
Example #19
Source File: GanttElastic.tsx    From react-gantt-elastic with MIT License 6 votes vote down vote up
reducer: Reducer<
  ImmutableObject<GanttElasticState>,
  { type: string; payload: any }
> = (state, action) => {
  const immutableState = Immutable.isImmutable(state)
    ? state
    : Immutable(state);
  if (action.type === "scroll") {
    // const scroll = Immutable.merge(state.scroll, );
    // return Immutable.merge(state, { ...action.payload });
  } else if (action.type === "update-calendar-height") {
    return immutableState.setIn(["calendar", "height"], action.payload);
  } else if (action.type === "resize") {
    return immutableState.setIn(["clientWidth"], action.payload);
  }
  return immutableState.merge(action.payload, { deep: true });
}
Example #20
Source File: api.ts    From contracts-ui with GNU General Public License v3.0 6 votes vote down vote up
apiReducer: Reducer<ApiState, ApiAction> = (state, action) => {
  switch (action.type) {
    case 'SET_ENDPOINT':
      return { ...state, endpoint: action.payload, error: null };

    case 'CONNECT_INIT':
      return { ...state, status: 'CONNECT_INIT' };

    case 'CONNECT':
      return { ...state, api: action.payload, error: null, status: 'CONNECTING' };

    case 'CONNECT_READY':
      return { ...state, ...action.payload, error: null, status: 'READY' };

    case 'CONNECT_ERROR':
      return { ...state, ...NULL_CHAIN_PROPERTIES, status: 'ERROR', error: action.payload };

    case 'LOAD_KEYRING':
      return { ...state, keyringStatus: 'LOADING' };

    case 'SET_KEYRING':
      return { ...state, keyring: action.payload, keyringStatus: 'READY' };

    case 'KEYRING_ERROR':
      return { ...state, keyringStatus: 'ERROR' };

    default:
      throw new Error(`Unknown action type`);
  }
}
Example #21
Source File: useLocalStorage.tsx    From Tachidesk-WebUI with Mozilla Public License 2.0 6 votes vote down vote up
export function useReducerLocalStorage<S, A>(
    reducer: Reducer<S, A>,
    key: string,
    defaultState: S | (() => S),
) {
    const [storedValue, setValue] = useLocalStorage(key, defaultState);
    return useReducer((state: S, action: A): S => {
        const newState = reducer(state, action);
        setValue(newState);
        return newState;
    }, storedValue);
}
Example #22
Source File: metamask.tsx    From metamask-snap-polkadot with Apache License 2.0 6 votes vote down vote up
reducer: Reducer<MetamaskState, MetamaskDispatch> = (state, action) => {
    switch (action.type) {
        case MetamaskActions.SET_INSTALLED_STATUS: {
            return {
                ...state,
                polkadotSnap: action.payload
            }
        }
        default: {
            return state;
        }
    }

}
Example #23
Source File: reducer.ts    From use-selected-items-hook with MIT License 5 votes vote down vote up
reducer: <Item>() => Reducer<State<Item>, Action<Item>> = () => (state, action) => {
  switch (action.type) {
    case ActionType.INITIALIZE_ITEMS: {
      const {
        initialSelectedItems = [],
        initialItems = [],
      } = action.payload;

      return update(state, {
        items: {
          $set: initialItems.map((item) => ({
            ...item,
            isSelected: initialSelectedItems.includes(item),
          })),
        },
      });
    }

    case ActionType.TOGGLE_SINGLE_ITEM: {
      const { items, itemIdentifierKey } = state;

      const { itemIdentifierValue } = action.payload;

      const itemIndex = items.findIndex((itemFound) => (
        itemFound[itemIdentifierKey] === itemIdentifierValue
      ));

      const item = items[itemIndex];

      return update(state, {
        items: {
          $splice: [[itemIndex, 1, {
            ...item,
            isSelected: !item.isSelected,
          }]],
        },
      });
    }

    case ActionType.TOGGLE_ALL_ITEMS: {
      return update(state, {
        items: {
          $set: state.items.map(item => ({
            ...item,
            isSelected: !item.isSelected,
          })),
        },
      });
    }

    default: {
      throw new Error("Unknown action type");
    }
  }
}
Example #24
Source File: hooks-core-utils.ts    From Chromogen with MIT License 5 votes vote down vote up
export function useHookedReducer<S, A>(
  reducer: Reducer<S, A>,
  initialState: S,
  store: EnhancedStore,
  reducerId: string | number,
): [S, Dispatch<A>] {
  
  const initialReducerState = useMemo(() => {
    const initialStateInStore = store.getState()[reducerId];
    return initialStateInStore === undefined ? initialState : initialStateInStore;
  }, []);

  const [localState, setState] = useState<S>(initialReducerState);

//Creating state property in store to save all state changes
store.subscribe(() => {
  hooksLedger.state = store.getState()[reducerId]
});

 //wrapped store.subscribe method inside useEffect to avoid listening to change exponentially
//  useEffect(() => {
//   const unsubscribe = store.subscribe(() => {
//     hooksLedger.state = store.getState()[reducerId]
//     console.log('hooks ledger after store is called on reducer id.', hooksLedger)
//   })
//   return unsubscribe;
//  }, [])
 
const dispatch = useMemo<Dispatch<A>>(() => {

  const dispatch = (action: any) => {     
      if (action && typeof action === 'object' && typeof action.type === 'string') {
        store.dispatch({
          type: `${reducerId}/${action.type}`,
          payload: action,
        });
      } else {
        //Subscribe will be called any time an action is dispatched, and some part of the state tree may potentially have changed. 
        store.subscribe(() => {
          hooksLedger.state = store.getState()[reducerId];;
          hooksLedger.id = reducerId;
          hooksLedger.initialState = hooksLedger.state;
          
          /* had to rid of state[0] to allow download function 
          hooksLedger.initialState = hooksLedger.state[0];*/
        });
       
        
        store.subscribe(() => {
          hooksLedger.currState = hooksLedger.state;
        });

        store.subscribe(() => {
          hooksLedger.dispCount = hooksLedger.dispCount + 1;
        });

        store.dispatch({
          type: reducerId,
          payload: action,
        });

    }
  }
   return dispatch;
  }, []);



  
  useEffect(() => {
    const teardown = store.registerHookedReducer(reducer, initialReducerState, reducerId);
    let lastReducerState = localState;
    const unsubscribe = store.subscribe(() => {
      const storeState: any = store.getState();
      const reducerState = storeState[reducerId];
      if (lastReducerState !== reducerState) {
        setState(reducerState);
      }
      lastReducerState = reducerState;
    });
    return () => {
      unsubscribe();
      teardown();
    };
  }, []);

  // Returns a tuple
  return [localState, dispatch];

}
Example #25
Source File: useExcalideckEditorState.ts    From excalideck with MIT License 5 votes vote down vote up
export default function useExcalideckEditorState(
    initialDeck: Deck,
    onDeckChange: (deck: Deck) => void
) {
    const [excalideckEditorState, dispatch] = useReducer<
        Reducer<ExcalideckEditorState, Action>
    >(excalideckEditorStateReducer, {
        activeView: View.Slides,
        deck: initialDeck,
        selectedSlideId: initialDeck.slides[0]!.id,
    });
    const deckHashRef = useRef(Hash.deck(excalideckEditorState.deck));

    useEffect(() => {
        if (deckHashRef.current !== Hash.deck(excalideckEditorState.deck)) {
            onDeckChange(excalideckEditorState.deck);
        }
    }, [onDeckChange, excalideckEditorState.deck]);

    return {
        activeView: excalideckEditorState.activeView,
        activateView(view: View) {
            dispatch(["activateView", view]);
        },

        deck: excalideckEditorState.deck,
        updatePrintableArea(printableArea: PrintableArea) {
            dispatch(["updatePrintableArea", printableArea]);
        },
        updateCommonExcalidrawElements(
            commonExcalidrawElements: ExcalidrawElement[]
        ) {
            dispatch([
                "updateCommonExcalidrawElements",
                commonExcalidrawElements,
            ]);
        },
        addEmptySlide() {
            dispatch(["addEmptySlide"]);
        },
        moveSlide(fromIndex: number, toIndex: number) {
            dispatch(["moveSlide", fromIndex, toIndex]);
        },
        deleteSlide(slideId: string) {
            dispatch(["deleteSlide", slideId]);
        },
        updateSlideShouldRender(slideId: string, shouldRender: boolean) {
            dispatch(["updateSlideShouldRender", slideId, shouldRender]);
        },
        updateSlideShouldRenderWithCommonExcalidrawElements(
            slideId: string,
            shouldRenderWithCommonExcalidrawElements: boolean
        ) {
            dispatch([
                "updateSlideShouldRenderWithCommonExcalidrawElements",
                slideId,
                shouldRenderWithCommonExcalidrawElements,
            ]);
        },
        updateSlideExcalidrawElements(
            slideId: string,
            excalidrawElements: ExcalidrawElement[]
        ) {
            dispatch([
                "updateSlideExcalidrawElements",
                slideId,
                excalidrawElements,
            ]);
        },

        selectedSlide: DeckOperations.getSlide(
            excalideckEditorState.deck,
            excalideckEditorState.selectedSlideId
        ),
        selectSlide(slideId: string) {
            dispatch(["selectSlide", slideId]);
        },
    };
}
Example #26
Source File: Management.tsx    From nodestatus with MIT License 5 votes vote down vote up
reducer: Reducer<typeof initialState, ActionType> = (state, action) => {
  const {
    mutate,
    form,
    installationScript = '',
    currentNode = ''
  } = action.payload ?? {};
  switch (action.type) {
    case 'showModal':
      return {
        ...state,
        showModal: true
      };
    case 'reverseSortEnabled':
      return { ...state, sortEnabled: !state.sortEnabled };
    case 'setInstallationScript':
      return { ...state, installationScript };
    case 'showImportForm': {
      return { ...state, showModal: true, isImport: true };
    }
    case 'setNode':
      return {
        ...state,
        showModal: true,
        currentNode,
        installationScript
      };
    case 'resetState':
      mutate?.();
      form?.resetFields();
      return {
        ...state,
        currentNode: '',
        installationScript: '',
        showModal: false,
        isImport: false
      };
    default:
      throw new Error();
  }
}
Example #27
Source File: use-connect.ts    From connect with GNU Lesser General Public License v3.0 5 votes vote down vote up
export function useConnect<T>(callback?: any, dependencies?: unknown[]): any {
  const { org, orgStatus } = useConnectContext()

  const [{ result, error, loading }, setStatus] = useReducer<
    Reducer<Status<T>, StatusAction<T>>
  >(statusReducer, statusInitial as Status<T>)

  if (callback === undefined) {
    callback = ((org) => org) as UseConnectCallback<Organization>
  }

  useEffect(() => {
    let cancelled = false
    let subscriptionHandler: SubscriptionHandler | null | undefined = null

    const update = async () => {
      setStatus({ type: 'loading' })

      try {
        if (!org) {
          if (orgStatus.error !== null) {
            setStatus({ type: 'error', error: orgStatus.error })
          }
          return
        }

        const result = await callback(org)

        if (cancelled || result === undefined) {
          return
        }

        // Subscription
        if (isSubscriptionStart<T>(result)) {
          subscriptionHandler = result((error: Error | null, result?: any) => {
            if (cancelled) {
              return
            }
            if (error) {
              setStatus({ type: 'error', error })
              return
            }
            setStatus({ type: 'done', result })
          })

          // Just async data
        } else {
          setStatus({ type: 'done', result })
        }
      } catch (err) {
        if (!cancelled) {
          setStatus({ type: 'error', error: err })
        }
      }
    }
    update()

    return () => {
      cancelled = true
      subscriptionHandler?.unsubscribe()
    }
  }, [org, ...(dependencies || [])])

  return useMemo(() => [result, { error, loading, retry: () => null }], [
    result,
    error,
    loading,
  ])
}
Example #28
Source File: TransactionsProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
transactionsCtxReducer: Reducer<State, Action> = (state, action) => {
  switch (action.type) {
    case Actions.Check: {
      const { id, blockNumber } = action.payload
      return {
        ...state,
        [id]: {
          ...state[id],
          blockNumberChecked: blockNumber,
        },
      }
    }

    case Actions.Finalize: {
      const {
        id,
        receipt: { status, blockNumber, transactionHash },
      } = action.payload
      return {
        ...state,
        [id]: {
          ...state[id],
          blockNumberChecked: blockNumber,
          hash: transactionHash,
          status: status === 1 ? TransactionStatus.Confirmed : TransactionStatus.Error,
        },
      }
    }

    case Actions.Response: {
      const {
        id,
        response: { hash, to, blockNumber },
      } = action.payload
      return {
        ...state,
        [id]: {
          ...state[id],
          status: TransactionStatus.Response,
          hash,
          to,
          blockNumber,
        },
      }
    }

    case Actions.Propose: {
      return {
        ...state,
        [action.payload.id]: {
          status: TransactionStatus.Pending,
          manifest: action.payload,
        },
      }
    }

    case Actions.Send: {
      const { id } = action.payload
      return {
        ...state,
        [id]: {
          ...state[id],
          status: TransactionStatus.Sent,
        },
      }
    }

    case Actions.Cancel: {
      const { [action.payload.id]: _, ...newState } = state

      return newState
    }

    case Actions.Error: {
      const { id, error } = action.payload
      return {
        ...state,
        [id]: {
          ...state[id],
          status: TransactionStatus.Error,
          error,
        },
      }
    }

    case Actions.Reset:
      return {}

    default:
      throw new Error('Unhandled action')
  }
}
Example #29
Source File: TokensProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
reducer: Reducer<State, Action> = (state, action) => {
  switch (action.type) {
    case Actions.SetFetched: {
      const fetched = action.payload
      return {
        ...state,
        tokens: fetched.reduce(
          (_tokens, { address, decimals, symbol, totalSupply }) => ({
            ..._tokens,
            [address]: {
              address,
              decimals,
              symbol: renameSymbol(symbol, address),
              balance: new BigDecimal(0, decimals),
              allowances: {},
              totalSupply: totalSupply as unknown as BigDecimal,
              ..._tokens[address],
            },
          }),
          state.tokens,
        ),
      }
    }

    case Actions.SubscribeBalance: {
      const { address, subId } = action.payload
      const sub = state.subscriptions[address]

      const balance = sub?.balance || new Set()

      if (balance.has(subId)) {
        return state
      }

      return {
        ...state,
        subscriptions: {
          ...state.subscriptions,
          [address]: {
            ...(sub as NonNullable<typeof sub>),
            balance: balance.add(subId),
          },
        },
      }
    }

    case Actions.UnsubscribeBalance: {
      const { address, subId } = action.payload
      const sub = state.subscriptions[address]

      const balance = sub?.balance || new Set()

      if (balance.has(subId)) {
        balance.delete(subId)
        return {
          ...state,
          subscriptions: {
            ...state.subscriptions,
            [address]: {
              ...(sub as NonNullable<typeof sub>),
              balance,
            },
          },
        }
      }

      return state
    }

    case Actions.SubscribeAllowance: {
      const { address, spender, subId } = action.payload
      const sub = state.subscriptions[address]

      const allowances = {
        ...sub?.allowances,
        [spender]: sub?.allowances?.[spender] || new Set(),
      }

      if (allowances[spender].has(subId)) {
        return state
      }

      return {
        ...state,
        subscriptions: {
          ...state.subscriptions,
          [address]: {
            ...(sub as NonNullable<typeof sub>),
            allowances: {
              ...allowances,
              [spender]: allowances[spender].add(subId),
            },
          },
        },
      }
    }

    case Actions.UnsubscribeAllowance: {
      const { address, spender, subId } = action.payload
      const sub = state.subscriptions[address]

      const allowances = {
        ...sub?.allowances,
        [spender]: sub?.allowances?.[spender] || new Set(),
      }

      if (allowances[spender].has(subId)) {
        allowances[spender].delete(subId)
        return {
          subscriptions: {
            ...state.subscriptions,
            [address]: {
              ...(sub as NonNullable<typeof sub>),
              allowances,
            },
          },
          tokens: state.tokens,
        }
      }

      return state
    }

    case Actions.Reset: {
      return {
        tokens: Object.keys(state.tokens).reduce(
          (_tokens, address) => ({
            ..._tokens,
            [address]: {
              ...(_tokens[address] as SubscribedToken),
              allowances: {},
              balance: new BigDecimal(0, (_tokens[address] as SubscribedToken).decimals),
            },
          }),
          state.tokens,
        ),
        subscriptions: {},
      }
    }

    case Actions.UpdateBalances: {
      const balances = action.payload
      return {
        subscriptions: state.subscriptions,
        tokens: Object.keys(balances).reduce(
          (_tokens, address) => ({
            ..._tokens,
            [address]: {
              ...(_tokens[address] as NonNullable<typeof _tokens[typeof address]>),
              balance: balances[address],
            },
          }),
          state.tokens,
        ),
      }
    }

    case Actions.UpdateAllowances: {
      const allowancesMap = action.payload
      return {
        ...state,
        tokens: Object.keys(allowancesMap).reduce(
          (_tokens, address) => ({
            ..._tokens,
            [address]: {
              ...(_tokens[address] as NonNullable<typeof _tokens[typeof address]>),
              allowances: {
                ..._tokens[address]?.allowances,
                ...allowancesMap[address],
              },
            },
          }),
          state.tokens,
        ),
      }
    }

    default:
      throw new Error('Unexpected action type')
  }
}