Over the years, I’ve spent countless hours building React applications for everything from small startups in San Francisco to large-scale enterprise systems. One thing I’ve noticed is that while React Context is fantastic for sharing data between components, it can feel like a walled garden when you need to access that data in a plain JavaScript file.
I often find myself needing to use context values, like an authentication token or a user’s preferred shipping address, inside a utility function or an Axios interceptor. Since these aren’t React components, you can’t simply reach for the useContext hook.
In this tutorial, I will show you exactly how to break out of that “walled garden” and access your context data from anywhere in your codebase.
The Problem with Context and Hooks
If you’ve been working with React for a while, you know that hooks have rules. You can only call them inside a functional component or another custom hook.
When you are inside a standard .js or .ts service file, perhaps for handling API calls, those hooks simply won’t work. React will throw a nasty error the moment you try to use them.
Method 1: The Singleton Reference Pattern
This is my go-to method when I need a quick and reliable way to sync my React state with an external service. I create a “ref holder” object that stays outside of the React lifecycle.
Here is the full code to implement this for a USA-based e-commerce shipping service:
// shipping-store.js
// This is our plain JS object that will hold the reference
export const ShippingStore = {
zipCode: '10001', // Default to NYC
updateZip: (newZip) => {
ShippingStore.zipCode = newZip;
}
};
// ShippingContext.js
import React, { createContext, useState, useEffect } from 'react';
import { ShippingStore } from './shipping-store';
export const ShippingContext = createContext();
export const ShippingProvider = ({ children }) => {
const [zipCode, setZipCode] = useState('10001');
// We sync the React state to the external store whenever it changes
useEffect(() => {
ShippingStore.updateZip(zipCode);
}, [zipCode]);
return (
<ShippingContext.Provider value={{ zipCode, setZipCode }}>
{children}
</ShippingContext.Provider>
);
};
// api-service.js
// Now we can access the zip code in a plain JS utility!
import { ShippingStore } from './shipping-store';
export const calculateShippingRates = async () => {
const currentZip = ShippingStore.zipCode;
console.log(`Calculating rates for US Zip: ${currentZip}`);
// Example API call logic
// const response = await fetch(`/api/rates?zip=${currentZip}`);
// return response.json();
};You can refer to the screenshot below to see the output.

I like this approach because it’s incredibly lightweight. You essentially create a bridge where the React component “pushes” the latest context value to an external object that your services can then read.
Method 2: The Observable (Event Bus) Pattern
Sometimes, simply reading a value isn’t enough. You might want your external service to trigger an update back into your React app. In these cases, I prefer using an Event Bus.
Here is how you can set up a global alert system:
// event-bus.js
const listeners = new Set();
export const EventBus = {
subscribe: (callback) => {
listeners.add(callback);
return () => listeners.delete(callback);
},
emit: (event, data) => {
listeners.forEach((callback) => callback(event, data));
}
};
// AlertContext.js
import React, { createContext, useState, useEffect } from 'react';
import { EventBus } from './event-bus';
export const AlertContext = createContext();
export const AlertProvider = ({ children }) => {
const [alert, setAlert] = useState(null);
useEffect(() => {
// We listen for events coming from outside of React
const unsubscribe = EventBus.subscribe((event, data) => {
if (event === 'SHOW_ALERT') {
setAlert(data);
}
});
return unsubscribe;
}, []);
return (
<AlertContext.Provider value={{ alert, setAlert }}>
{children}
{alert && <div className="alert-banner">{alert}</div>}
</AlertContext.Provider>
);
};
// external-logger.js
import { EventBus } from './event-bus';
export const logGlobalError = (errorMsg) => {
console.error("System Error:", errorMsg);
// Trigger a UI alert from a plain JS function!
EventBus.emit('SHOW_ALERT', `System Error: ${errorMsg}. Please contact support.`);
};You can refer to the screenshot below to see the output.

I’ve used this many times for handling global notifications or logging systems in large-scale apps. It allows a separate part of your app to “broadcast” a message that your Context Provider is listening for.
Method 3: Export the Provider’s Dispatcher
In more advanced scenarios, I like to export a function that captures the “dispatch” or “setter” function from the provider. This is a bit of a “hacky” favorite of mine because it gives you direct control.
// auth-bridge.js
let logoutFunction = () => {
console.warn("Logout function not initialized yet.");
};
export const setGlobalLogout = (fn) => {
logoutFunction = fn;
};
export const triggerGlobalLogout = () => {
logoutFunction();
};
// AuthContext.js
import React, { createContext, useState, useEffect } from 'react';
import { setGlobalLogout } from './auth-bridge';
export const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState({ name: "Alex", role: "Admin" });
const logout = () => {
console.log("Logging out user...");
setUser(null);
};
// Register the logout function with our bridge
useEffect(() => {
setGlobalLogout(logout);
}, []);
return (
<AuthContext.Provider value={{ user, logout }}>
{children}
</AuthContext.Provider>
);
};
// axios-interceptor.js
import axios from 'axios';
import { triggerGlobalLogout } from './auth-bridge';
const api = axios.create({ baseURL: 'https://api.myapp.com' });
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && error.response.status === 401) {
// Accessing and executing a context function outside of a component!
triggerGlobalLogout();
}
return Promise.reject(error);
}
);You can refer to the screenshot below to see the output.

I usually use this for authentication managers where a 401 Unauthorized response from an API needs to clear the user’s session in the React state immediately.
Method 4: Use the Context Internal Object (Not Recommended)
There is a way to access the current value directly from the Context object itself using Context._currentValue. However, I strongly advise against this for production apps.
In my experience, relying on internal React properties (those starting with an underscore) is a recipe for disaster. If the React team changes how context works internally in a future update, your app will break. Always stick to the explicit patterns I mentioned above for a more robust architecture.
While it is tempting to find a “shortcut,” the Singleton or Event Bus patterns are much safer and easier for other developers on your team to understand.
In this tutorial, I have covered several ways to access React Context outside of a component. For most use cases, the Singleton Reference pattern (Method 1) is the most straightforward and easiest to maintain. It provides a clean bridge between your React UI and your business logic.
If you are working on a very large application with complex event handling, the Event Bus (Method 2) might be a better fit. Regardless of which method you choose, remember to clean up your listeners and effects to avoid memory leaks.
You may also read:
- Implement Scroll to Component in React
- Parent-Child Component Communication in React
- React Render Component on Button Click
- Ways to Display JSON in React

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.