Introduction
In the world of web development, it's not uncommon to find yourself needing to integrate React, a popular JavaScript library for building user interfaces, with non-React code. This could be due to a variety of reasons such as working with legacy code, using third-party libraries that aren't built with React, or simply needing to interact with the raw DOM API or other technologies like WebSockets...
In this blog post. We'll learn how to create a external store, what they do, why they're useful, and how to get the most out of them .
Let's get started!
A external store
A external store is a JavaScript object that stores data and provides methods for reading and updating that data. It's a simple concept, but it can be used to solve some interesting problems .
Alright, let's start with the following setup:
It's relatively short , but there's a lot of stuff packed into this small function, let's break it down:
- Store state
The first thing is store's state. This is where we'll store all of the data that our store needs to keep track of, and a function called getState
that returns the current state of the store.
- Listeners
Next, we create a variable called listeners that will hold an array of functions. These functions are called whenever the store's state changes, and they're responsible for updating any components that are subscribed to the store.
- Dispatch
A dispatch function takes an action as an argument. This function is responsible for updating the store's state based on the action that was passed in. It does this by calling the reducer function that was passed into the createStore function when it was created and update any components that are subscribed to the store by calling the emitChange
function.
- Subscribe
The last thing we do is create a subscribe function that takes a listener as an argument. This function is responsible for adding the listener to the listeners array and returning a function that can be used to remove the listener from the array.
You may find this a bit similar to the Redux Application Data flow:
This often referred to as the Pub-Sub pattern, is a popular design pattern in JavaScript for managing and decoupling code in an application. It promotes loose coupling by establishing a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. This is achieved by publishers
who send out notifications or events, and subscribers
who listen for these events and react accordingly .
Use cases
Now that we have a basic understanding of what a external store is and how it works, let's look at some use cases where it can be useful .
Sharing data different parts of application
One of the most common use cases for a external store is sharing data between different parts of an application, allow us to interact with some third-party libraries that hold state outside of React or browser API
For example, In axios library, we can use axios.interceptors
to intercept requests or responses before they are handled by then
or catch
.
In most of projects I've worked on, it use an authentication token to authenticate requests, so by creating an instance of axios with some custom configs along with interceptors, and then export it to use in other parts of the application, i can easily handle once the token is expired or invalid by using the refresh token to get a new one.
But the refresh token itself has many security issues, so many projects only provide a short-lived token, and when it expires, the user must log in again and thus lose all the data they have entered, creating a bad user experience.
So, how can we handle this case?
We can show a popup to ask user to login again while keep their current page in the background, and once they login successfully, retrying all the failed request. But the axios instance is created outside of React, how can we show a popup that is a React component and handling all the login logic?
This is where a external store comes in handy. We can use it to store a state that control the popup visibility and dispatch an action to toggle it when the token is expired.
And then we can use it in our React component like this:
Update our interceptor accordingly:
By using this pattern, we can easily handle the case where the token is expired without losing any data that the user has entered.
Appendix: Tweaks
There are a few more small tweaks and optimizations I've made to the solution. Let's talk about them!
- Custom hook
In our example, we used useEffect
to subscribe to the store and update the component's state whenever the store's state changes. This works fine, but it's not very elegant. We can do better by creating a custom hook that handles this for us.
And then we can use it in our component like this:
- Store actions
When we need a dispatch an action, we have to call store.dispatch
and pass in the action type and payload. But we can create an object that contains all of our store's actions and then export it so that we can use it in other parts of our application.
This makes it easier to dispatch actions without having to import the store directly.
Conclusion
In this article, we've learned how to create a external store, what they do, why they're useful. We also looked at some use cases where they can be helpful in solving problems that would otherwise be difficult or impossible to solve without them.
I hope you found this article helpful! If you have any questions or feedback, please reach out to me on social media.
Happy reading!