I recently have been using hooks a lot lately, and I’ve really been enjoying the ease of it. The way you can write re-usable custom hooks, easily convert a stateless component into a stateful component (while still functional), and the simplicity of useEffect have made the transition to hooks very enjoyable.
When it comes to global state management, Redux has long been the #1 choice. I’ve used it on many projects, and it is definitely incredibly useful. However, depending on the complexity of your state management, you may not need Redux. There is a lot of work involved each time you connect a component, write a saga, etc. At work, we have completely gone away from Redux, and we are simply using
useContext to manage our global state. To demonstrate this, I have a simple app using the Star Wars API. Below is my setup.
And then to render the data in a component, we simply call
useContext and pass in the Context that we exported from
context/index.js. No we can access
dispatch from that context.
This is certainly a simple implementation of context, but it illustrates essentially how you can achieve a similar effect as you might be used to with Redux.
The two biggest drawbacks I found at first with saying goodbye to Redux were:
- No dev tools
- Not being able to easily use something like redux-saga to take the latest action
There are a couple of ways you can get around these two issues. For the redux dev tools, you can still connect to the dev tools, but setting it up in such a way that you can time travel through your actions is a bit more difficult. By making a few adjustments to your reducer, this solution below will at least allow you to see your data in one place, at the expense of the time slicing feature.
The second issue is being able to take the latest request. Let’s say you have a list of users. You click
user1, but then quickly click
user2. Now you have fired two API requests, and it is a race to see which one returns first (or really last). If
user2 comes back before
user1, by the time
user1 returns, the reducer will then update that response into context. This would lead to a very frustrating and confusing UX. Ideally, if the user clicks
user1 we want to ignore the response for
user1. Here is my solution for this issue.
I made a Fetcher class that handles the API calls and the context dispatch. If you notice, I am creating a requestId and setting it as an instance variable on the class. This way, every time a request occurs, the request id gets updated. When the response comes back, only dispatch the response if the id is still valid.
Here it is in action. The “Get Slow” buttons have a 5 second delay on the response.
This might not be the fanciest or most elegant solution, but it is working well for my needs.
If you want to see the entire demo, you can find the repo at https://github.com/austinChappell/reducer-take-latest-demo