5

I learned react and Redux at the same time and went "all in" on Redux; basically all state is stored in Redux. And I followed the standard allIds, byId state shape pattern as detailed here.

My app is very data-centric, it talks to an API, and does alot of CRUD type actions - fetchAll, fetchById, add, update, delete.

The API communication is segregated into a "service layer" module that is its own npm package. All calls to this service layer are in the Redux actions, using redux-thunk.

I've realized there is no need to put most everything in Redux, the data is really needed on a specific component, for example. And I would love to simplify this architecture.

So I began to refactor into a custom hook instead. It seemed since my state shape was more of an object rather than scalar, I should use useReducer rather than useState...

// reducer
// ------------------------- 
const initialState = {
  adding: false,
  updating: false,
  deleting: false,
  error: null,
  items: null
};
const reducer = (state, action) => {
// implementation omitted for brevity. . .
}
const useItemsApi = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  // wrapped in useCallback because called in component's useEffect
  const fetchItems = useCallback(async (options) => {
    try {
      const resp = apiService.fetchItems(options);
    } catch (err) {
      if(err.status === 401) 
         // send to login screen
      else
         dispatch({type: 'error', payload: err});
    }
  }, [options]);

  // addItem, updateItem, deleteItem, etc...

  const actions = {fetchItems, updateItem, addItem, deleteItem};
  return [state, actions];
};

// component
// ------------------------- 
const component = (props) => {
  const [state, actions] = useItemsApi();
  const {fetchItems, updateItem, addItem, deleteItem} = actions;
  useEffect(() => {
     fetchItems()
  }, fetchItems);

  // omitted for brevity...
}

When I got to setting the state in the reducer for the update action, I realized it would be easier if I used "allIds" and "byId" pattern.

And at this point I thought - how is this any different than using Redux?

It is going to end up looking like almost the exact same code, and I'm losing some power of selectors, but removing the complexity of redux-thunks. And my current redux actions include specific use case actions (special save for item type X, for ex.) so I'd need to find a place for those.

My question is - is there any reason to refactor this to a hook using local state?

user210757
  • 5,604
  • 12
  • 54
  • 94
  • It's the same pattern, but used locally. So it is different since it's no longer globally accessible. If the pattern feels like too much boilerplate, you could fall back to Class components, hooks are not the solution to everything. Class components aren't deprecated. – Emile Bergeron Nov 08 '19 at 16:24
  • @EmileBergeron I'm fine with the boilerplate and am loving hooks, just haven't seen alot of examples of what I'm doing basically making local state redux. Seems like more examples of just putting fetch right in the components – user210757 Nov 22 '19 at 22:11
  • Are you basically asking why would you use just React when React and Redux work fine? Or are you asking why use Redux when React works just fine by itself? – Galupuf Nov 25 '19 at 20:18
  • @Galupuf I guess I'm asking if I'm using hooks and local state correctly by using it the exact same way I'd use redux – user210757 Nov 27 '19 at 15:07
  • 1
    Yes you are :) Keep in mind that `useReducer()` was actually inspired by redux and you should use it if "you have complex state logic that involves multiple sub-values or when the next state depends on the previous one." Keep in mind you can also use `useState()`, but it just depend on your use case. – Galupuf Nov 27 '19 at 15:13
  • `redux-api-middleware` would be a much better solution – David Bradshaw Nov 27 '19 at 23:39

3 Answers3

2

It really boils to three things for me:

  1. Whether you need Redux's middleware, logging features etc or not
  2. How much you care about future stability
  3. Personal taste

Redux offers more than merely state management.
Offloading context handling to Redux is a big plus for me looking forward into the future.
If your application is very data-centric, I would not omit redux-devtools and other middleware (I personally like redux-observable).
When your app grows in complexity you will want to find out about corrupt state updates and state that gets triggered multiple times unexpectedly.
But then again, only you can assess the complexity of your own app and where it will be headed towards in the future.

The rest of this post is 'personal taste', but I'll add it.

Personally I like using Redux for a few different reasons than before mentioned.
I used Redux without using React at all not even so long ago, and also with a framework which nobody probably heard about, which is the Lightning web components framework from Salesforce.
The point being that it keeps state management and view logic in separate libraries.
React becoming a Swiss army knife is something I'm not really in favour of, personally.
React's core strength for me was that it was a view library featuring the virtual DOM with a clear purpose whereas now... well where is the border between it just becoming an opinionated framework?
Using React hooks is not imposed, but then again it sort of is.
If you use React, you will use all of it, this question bringing tribute to this conclusion.

And at this point I thought - how is this any different than using Redux?

So you refactor Redux to the useReducer hook and then wonder, why did I need this? If you ask then you probably didn't.
Maybe that is just the answer to your question.
Reducer functionality just moved from a state management library to a view library (or is it?). Cool (I guess).

html_programmer
  • 14,612
  • 12
  • 59
  • 125
1

Advantages of storing the state in Redux:

  • You can access and modify it globally
  • It persists even after your component is unmounted

Advantages of storing the state in the component:

  • You can have multiple components with different values in the state, which may be something you want
  • ...Or you could even have multiple hooks of the same type in one component!
  • You don't need to switch between files. Depending on how your code is organized, Redux can be split into 3 files + 1 file for the component which uses it - while this can help keep your code well-structured for complex use cases, it can be an overkill for keeping track of a simple state. Having to switch between multiple files to work on one component can reduce your productivity (I don't like having to keep track of 4 tabs in my IDE for every feature I work on).
  • (Also, hooks are new and cool.)

So, use Redux if:

  • You need to share state between multiple components (or plan to in the future)
  • You need to keep state even when the component that uses it is unmounted

You might prefer to keep the state in React (hooks or otherwise) in other cases since they simplify your code a bit.

But that doesn't mean you need to refactor your entire codebase. If you think your code is concise enough and you like the way it is organized, or if you are unsure if you will need the state globally in the future, you can keep it in Redux - there is nothing wrong with that!

Reinis Mazeiks
  • 521
  • 1
  • 4
  • 13
1

No, you don't have to. From my point of view, if only as a state management lib, Redux can be replaced by Hooks (useReducers ect) + local/shared state.

Redux comes before hooks and our app has been implemented by Redux, definitely you don't have the need to replace it.

We can plan to use hooks + shared state as an alternative in our new projects.

I did met with three situations, which are:

  • Redux ONLY;
  • Hooks ONLY;
  • Redux + Hooks;

All of them worked fine.

It's the dilemma that we're in the transition of Redux and Hooks. Hooks are made in a way not to replace redux but it can if you want to.

So in future, you can use any of those, depending on you use case.

Hope it helps.

jian
  • 11
  • 2