# Configuring useReducer with redux-devtools and thunk actions.

1. Creating a custom React useReducer hook to support passing functions as actions.

```
import { useReducer } from 'react'

function useReducerWithThunk(reducer, initialState) {
  const [state, dispatch] = useReducer(reducer, initialState);
  
  let customDispatch = (action) => {
      if (typeof action === 'function') {
        action(customDispatch);
      } else {
        dispatch(action); 
      }
    };
  return [state, customDispatch];
}

``` 
If the type of action is a function instead of an object, we are calling it by passing it our own custom dispatch.


2. Using the above useReducer hook.

```
import useReducerWithThunk from "./useReducerWithThunk";

const thunk = () => async (dispatch) => {
  dispatch({ type:"FETCH" });
}

export default function App() {
  const [count, dispatch] = useReducerWithThunk(reducer, 0);
    
  function handleClick() {
    dispatch(thunk());
  }
    
  return <button onClick={handleClick}>Click me!</button>
}

``` 

## Adding Redux devtools support

Before that, we need to consider the below points.
- Devtools should support redux’s store along with React userReducer’s.
- The useReducer is completely isolated and we can’t combine them to create a rootReducer which is what we are trying to avoid.
- But, we can create a separate store for each useReducer in dev-tools and switch between them using the Select instance dropdown shown below.

![c0ab9126cbf263a0442b7a39417275d63c5dafca-712x205.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1607449538675/b5MLYo94Y.png)

## Implementation

```
import { useReducer, useMemo, useEffect } from "react";

let stores = {};
let subscribers = {};

const REDUX_DEVTOOL_SET_STATE = "REDUX_DEVTOOL_SET_STATE";
const withDevTools = (name) => {
  return (
    name &&
    process.env.NODE_ENV === "development" &&
    typeof window !== "undefined" &&
    window.__REDUX_DEVTOOLS_EXTENSION__
  );
};

const devToolReducer = (reducer) => (state, action) => {
  if (action.type === REDUX_DEVTOOL_SET_STATE) {
    return action.state;
  } else {
    return reducer(state, action);
  }
};

function useReducerWithThunk(reducer, initialState, name) {
  let memoizedReducer = reducer;
  let shouldConfigDevTools = withDevTools(name);
  const nameWithUniqueNameSpace = getReducerName(name);

  // Memoizing to prevent recreation of devtoolReducer on each render.
  if (shouldConfigDevTools) {
    memoizedReducer = useMemo(() => devToolReducer(reducer), [reducer]);
  }

  const [state, dispatch] = useReducer(memoizedReducer, initialState);

  useEffect(() => {
    if (shouldConfigDevTools) {
      if (stores[name]) {
        throw new Error("More than one useReducerWithThunk have same name");
      }

      stores[nameWithUniqueNameSpace] = window.__REDUX_DEVTOOLS_EXTENSION__(
        reducer,
        initialState,
        {
          name: nameWithUniqueNameSpace,
        }
      );

      subscribers[nameWithUniqueNameSpace] = stores[
        nameWithUniqueNameSpace
      ].subscribe(() => {
        dispatch({
          type: REDUX_DEVTOOL_SET_STATE,
          state: stores[nameWithUniqueNameSpace].getState(),
        });
      });
    }

    return () => {
      if (shouldConfigDevTools) {
        subscribers[nameWithUniqueNameSpace]();
        subscribers[nameWithUniqueNameSpace] = undefined;
        stores[nameWithUniqueNameSpace] = undefined;
      }
    };
  }, []);

  const getState = () => state;

  const customDispatch = (action) => {
    if (typeof action === "function") {
      return action(customDispatch, getState);
    } else {
      if (shouldConfigDevTools && stores[nameWithUniqueNameSpace]) {
        stores[nameWithUniqueNameSpace].dispatch(action);
      } else {
        dispatch(action);
      }
    }
  };

  return [state, customDispatch];
}

const getReducerName = (name) => {
  return "userReducerThunk_" + name;
};

export default useReducerWithThunk;
``` 

## Usage

The above useReducer — React redux hook can be used as below.

```
const [count, dispatch] = useReducerWithThunk(reducer, 0, name // optional);
```

## Result

![71a9edc08921c380ddddee0502379a42a2cea6a3-1229x691.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1607449599889/JY0CoFnUl.jpeg)

## End Notes

- It would be much better if only a few complicated reducers are connected to Redux devtools or else the overhead involved in switching and finding the appropriate instance will exceed the benefits.
- But at least the reason for not able to use dev tools should not prevent you from keeping the state local.

[Source](https://github.com/intergalacticspacehighway/use-reducer-thunk)

[npm](https://www.npmjs.com/package/use-reducer-thunk) 

## References
[Article](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) by [Mihail Diordiev](https://twitter.com/mdiordiev)

[Redux devtools documentation](https://github.com/reduxjs/redux-devtools)
