Event-Driven Architecture for Clean React Component Communication
Nicola

Nicola @nicolalc

About: Sr. Frontend Software Engineer, creating user interfaces for Game and Web applications. UX and design passionate.

Location:
Italy
Joined:
Aug 5, 2019

Event-Driven Architecture for Clean React Component Communication

Publish Date: Dec 4 '24
257 45

Are you tired of the endless tangle of props drilling and callback chains in your React applications? Does managing state and communication between deeply nested components feel like wrestling with spaghetti code?

An event-driven architecture can simplify your component interactions, reduce complexity, and make your app more maintainable. In this article, I’ll show you how to use a custom useEvent hook to decouple components and improve communication across your React app.

Let me walk you through it, let's start from

Note: This mechanism is not intended as a replacement for a global state management system but an alternative approach to component communication flow.


The Problem: Props Drilling and Callback Chains

In modern application development, managing state and communication between components can quickly become cumbersome. This is especially true in scenarios involving props drilling—where data must be passed down through multiple levels of nested components—and callback chains, which can lead to tangled logic and make code harder to maintain or debug.

These challenges often create tightly coupled components, reduce flexibility, and increase the cognitive load for developers trying to trace how data flows through the application. Without a better approach, this complexity can significantly slow down development and lead to a brittle codebase.

The Traditional Flow: Props Down, Callbacks Up

In a typical React application, parent components pass props to their children, and children communicate back to the parent by triggering callbacks. This works fine for shallow component trees, but as the hierarchy deepens, things start to get messy:

Props Drilling: Data must be passed down manually through multiple levels of components, even if only the deepest component needs it.

Callback Chains: Similarly, child components must forward event handlers up the tree, creating tightly coupled and hard-to-maintain structures.

A Common Problem: Callback Complexity

Take this scenario, for example:

  • The Parent passes props to Children A.
  • From there, props are drilled down to GrandChildren A/B and eventually to SubChildren N.
  • If SubChildren N needs to notify the Parent of an event, it triggers a callback that travels back up through each intermediate component.

This setup becomes harder to manage as the application grows. Intermediate components often act as nothing more than middlemen, forwarding props and callbacks, which bloats the code and reduces maintainability.

PropsDrilling and CallbackChains

To address props drilling, we often turn to solutions like global state management libraries (e.g., Zustand) to streamline data sharing. But what about managing callbacks?

This is where an event-driven approach can be a game-changer. By decoupling components and relying on events to handle interactions, we can significantly simplify callback management. Let’s explore how this approach works.


The Solution: Enter the Event-Driven Approach

Event lifecycle

Instead of relying on direct callbacks to communicate up the tree, an event-driven architecture decouples components and centralizes communication. Here’s how it works:

Event Dispatching

When SubChildren N triggers an event (e.g., onMyEvent), it doesn’t directly call a callback in the Parent.
Instead, it dispatches an event that is handled by a centralized Events Handler.

Centralized Handling

The Events Handler listens for the dispatched event and processes it.
It can notify the Parent (or any other interested component) or trigger additional actions as required.

Props Remain Downward

Props are still passed down the hierarchy, ensuring that components receive the data they need to function.

This can be solved with centralized state management tools like zustand, redux, but will not be covered in this article.


Implementation

But, how do we implement this architecture?

useEvent hook

Let's create a custom hook called useEvent, this hook will be responsible of handling event subscription and returning a dispatch function to trigger the target event.

As I am using typescript, I need to extend the window Event interface in order to create custom events:

interface AppEvent<PayloadType = unknown> extends Event {
  detail: PayloadType;
}

export const useEvent = <PayloadType = unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<PayloadType> | VoidFunction
) => {
  ...
};
Enter fullscreen mode Exit fullscreen mode

By doing so, we can define custom events map and pass custom parameters:

interface AppEvent<PayloadType = unknown> extends Event {
  detail: PayloadType;
}

export interface CustomWindowEventMap extends WindowEventMap {
  /* Custom Event */
  onMyEvent: AppEvent<string>; // an event with a string payload
}

export const useEvent = <PayloadType = unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<PayloadType> | VoidFunction
) => {
  ...
};
Enter fullscreen mode Exit fullscreen mode

Now that we defined needed interfaces, let's see the final hook code

import { useCallback, useEffect, type Dispatch } from "react";

interface AppEvent<PayloadType = unknown> extends Event {
  detail: PayloadType;
}

export interface CustomWindowEventMap extends WindowEventMap {
  /* Custom Event */
  onMyEvent: AppEvent<string>;
}

export const useEvent = <PayloadType = unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<PayloadType> | VoidFunction
) => {
  useEffect(() => {
    if (!callback) {
      return;
    }

    const listener = ((event: AppEvent<PayloadType>) => {
      callback(event.detail); // Use `event.detail` for custom payloads
    }) as EventListener;

    window.addEventListener(eventName, listener);
    return () => {
      window.removeEventListener(eventName, listener);
    };
  }, [callback, eventName]);

  const dispatch = useCallback(
    (detail: PayloadType) => {
      const event = new CustomEvent(eventName, { detail });
      window.dispatchEvent(event);
    },
    [eventName]
  );

  // Return a function to dispatch the event
  return { dispatch };
};

Enter fullscreen mode Exit fullscreen mode

The useEvent hook is a custom React hook for subscribing to and dispatching custom window events. It allows you to listen for custom events and trigger them with a specific payload.

What we are doing here is pretty simple, we are using the standard event management system and extending it in order to accommodate our custom events.

Parameters:

  • eventName (string): The name of the event to listen for.
  • callback (optional): A function to call when the event is triggered, receiving the payload as an argument.

Features:

  • Event Listener: It listens for the specified event and calls the provided callback with the event's detail (custom payload).
  • Dispatching Events: The hook provides a dispatch function to trigger the event with a custom payload.

Example:

const { dispatch } = useEvent("onMyEvent", (data) => console.log(data));

// To dispatch an event
dispatch("Hello, World!");

// when dispatched, the event will trigger the callback
Enter fullscreen mode Exit fullscreen mode

Ok cool but, what about a

Real World Example?

Check out this StackBlitz (if it does not load, please check it here)

This simple example showcases the purpose of the useEvent hook, basically the body's button is dispatching an event that is intercepted from Sidebar, Header and Footer components, that updates accordingly.

This let us define cause/effect reactions without the need to propagate a callback to many components.

Note
As pointed out in the comments remember to memoize the callback function using useCallback in order to avoid continous event removal and creation, as the callback itself will be a dependency of the useEvent internal useEffect.


Real-World Use Cases for useEvent

Here are some real-world use cases where the useEvent hook can simplify communication and decouple components in a React application:


1. Notifications System

A notification system often requires global communication.

  • Scenario:

    • When an API call succeeds, a "success" notification needs to be displayed across the app.
    • Components like a "Notifications Badge" in the header need to update as well.
  • Solution: Use the useEvent hook to dispatch an onNotification event with the notification details. Components like the NotificationBanner and Header can listen to this event and update independently.

2. Theme Switching

When a user toggles the theme (e.g., light/dark mode), multiple components may need to respond.

  • Scenario:

    • A ThemeToggle component dispatches a custom onThemeChange event.
    • Components like the Sidebar and Header listen for this event and update their styles accordingly.
  • Benefits: No need to pass the theme state or callback functions through props across the entire component tree.

3. Global Key Bindings

Implement global shortcuts, such as pressing "Ctrl+S" to save a draft or "Escape" to close a modal.

  • Scenario:
    • A global keydown listener dispatches an onShortcutPressed event with the pressed key details.
    • Modal components or other UI elements respond to specific shortcuts without relying on parent components to forward the key event.

4. Real-Time Updates

Applications like chat apps or live dashboards require multiple components to react to real-time updates.

  • Scenario:
    • A WebSocket connection dispatches onNewMessage or onDataUpdate events when new data arrives.
    • Components such as a chat window, notifications, and unread message counters can independently handle updates.

5. Form Validation Across Components

For complex forms with multiple sections, validation events can be centralized.

  • Scenario:
    • A form component dispatches onFormValidate events as users fill out fields.
    • A summary component listens for these events to display validation errors without tightly coupling with form logic.

6. Analytics Tracking

Track user interactions (e.g., button clicks, navigation events) and send them to an analytics service.

  • Scenario:
    • Dispatch onUserInteraction events with relevant details (e.g., the clicked button’s label).
    • A central analytics handler listens for these events and sends them to an analytics API.

7. Collaboration Tools

For collaborative tools like shared whiteboards or document editors, events can manage multi-user interactions.

  • Scenario:
    • Dispatch onUserAction events whenever a user draws, types, or moves an object.
    • Other clients and UI components listen for these events to reflect the changes in real time.

By leveraging the useEvent hook in these scenarios, you can create modular, maintainable, and scalable applications without relying on deeply nested props or callback chains.


Conclusions

Events can transform the way you build React applications by reducing complexity and improving modularity. Start small—identify a few components in your app that would benefit from decoupled communication and implement the useEvent hook.

With this approach, you’ll not only simplify your code but also make it easier to maintain and scale in the future.

Why Use Events?
Events shine when you need your components to react to something that happened elsewhere in your application, without introducing unnecessary dependencies or convoluted callback chains. This approach reduces the cognitive load and avoids the pitfalls of tightly coupling components.

My Recommendation
Use events for inter-component communication—when one component needs to notify others about an action or state change, regardless of their location in the component tree.
Avoid using events for intra-component communication, especially for components that are closely related or directly connected. For these scenarios, rely on React's built-in mechanisms like props, state, or context.

A Balanced Approach
While events are powerful, overusing them can lead to chaos. Use them judiciously to simplify communication across loosely connected components, but don’t let them replace React’s standard tools for managing local interactions.

Comments 45 total

  • Mike Talbot ⭐
    Mike Talbot ⭐Dec 5, 2024

    Do you not need deps on the useEvent? Or is it rebinding every redraw? I have a very similar system, and needed that for it.

    • Nicola
      NicolaDec 5, 2024

      Thanks Mike for pointing that out, for sure something I need to add to the article.

      As the callback itself is part of the useEffect deps array:

        useEffect(() => {
          [...]
        }, [callback, eventName]);
      
      Enter fullscreen mode Exit fullscreen mode

      it works like any classic hook dependency, and I suggest to memoize it else it will result in a new function at every re-render as in javascript functions are not comparable.

      What I usually do is that (until React 19 compiler at least):

      const myCallback = useCallback([...], [deps])
      
      useEvent("eventName", myCallback);
      
      Enter fullscreen mode Exit fullscreen mode

      By doing so you will pass a memoized function to the hook, and if I'm not wrong (need to check that but pretty sure it works) the ref to that function will not change and the effect not triggered again.

      • Mike Talbot ⭐
        Mike Talbot ⭐Dec 5, 2024

        Yeah that's a way! I also ended up wiring the event to my global dispatcher in a useMemo and removing it in the release function of a useEffect to get it there quickly as I raise quite a few lifecycle events and occasionally they were missed. Due to this I also capture events before the component is mounted and then dispatch them when it is.

        export function useEvent(eventName, handler, deps = []) {
            const mounted = useRef()
            const mountFn = useRef(noop)
            const remove = useRef(noop)
            // eslint-disable-next-line react-hooks/exhaustive-deps
            const innerHandler = useCallback(readyHandler, [...deps, eventName])
        
            innerHandler.priority = handler.priority
            useMemo(() => {
                remove.current()
                remove.current = handle(eventName, innerHandler)
                // eslint-disable-next-line react-hooks/exhaustive-deps
            }, [...deps, eventName])
            useEffect(() => {
                mounted.current = true
                mountFn.current()
                return () => {
                    if (remove.current) {
                        remove.current()
                        remove.current = noop
                    }
                    if (mounted.current) {
                        mounted.current = false
                    }
                }
            }, [eventName])
            return handler
        
            function readyHandler(...params) {
                const self = this
                if (mounted.current) {
                    return handler.call(self, ...params)
                }
                const existing = mountFn.current
                mountFn.current = () => {
                    try {
                        existing()
                    } catch (e) {
                        //
                    }
                    handler.call(self, ...params)
                }
                return undefined
            }
        }
        
        Enter fullscreen mode Exit fullscreen mode
  • Vincenzo
    VincenzoDec 5, 2024

    this looks like it will get messy quickly depending on what type of state you need to maintain.

    a state management solution would be superior to this even if the state is simpler, less code and future proof in case it needs to grow.

    • Nicola
      NicolaDec 5, 2024

      Hi Vincenzo, thanks for your comment. Are you talking about the cloned [count, setCount] state management? If so it's just for example purposes and not the right use-case for useEvent hook, it's just to demonstrate how to communicate between components and no callbacks at all. I will change the example if that's the case as I agree it might be confusing.

      • Vincenzo
        VincenzoDec 5, 2024

        even if it wasn't just that, the event system doesn't really scale well for shared state.

        • Nicola
          NicolaDec 5, 2024

          Indeed, as I wrote at the start of the article, to manage shared state there are solutions like Zustand or Redux, the event system is intended for communication, not for sharing data.

          Think about it like: "Something happened elsewhere, and I need to react accordingly".

          Hope this clarify its responsibility.

          • chandragie
            chandragieDec 5, 2024

            I was thinking the same as him. Thought that Redux etc would be enough. But I remember you highlighted the callback chains bottom up. Thanks for the example.

    • Truong Dung
      Truong DungMar 3, 2025

      I would like to know which is different from Zustand.

  • John Costanzo
    John CostanzoDec 5, 2024

    A thing to consider here is if someone has your app opened in multiple tabs. I could be mistaken but that window event would reach both tabs

    • chandragie
      chandragieDec 5, 2024

      But, isn't that a good thing? 🤔 If I open a same page in two tabs (purposely or not), then if I clicked on some action (say, add to cart), I would also want the cart being updated in the idle tab. Am I imagining it wrong?

      • Aman Arya
        Aman AryaDec 5, 2024

        Not exactly. The problem arises when actions from both tabs start interfering with each other. For example, if you're editing a document in two tabs and save different changes in both, merging them could lead to conflicts or data loss. It’s better to keep actions isolated per tab to maintain consistency and avoid unexpected behavior.

    • Nicola
      NicolaDec 7, 2024

      Thanks John, indeed I need to check that,I would prefer to keep tabs independent from each others but I need to check how does this works in that case. Anyway, the solution to that should be simple by just triggering the events within the document context instead of the window, but smth to look at

  •  Business Directory Sites
    Business Directory SitesDec 5, 2024

    Great work!

  • Harsh
    HarshDec 5, 2024

    Bro the font family is not readable.

    • Nicola
      NicolaDec 7, 2024

      Thanks Harsh, you mean the graphs font?

  • Alain D'Ettorre
    Alain D'EttorreDec 6, 2024

    The browser-native CustomEvent is great and is also the standard in web components. However, keeping track of those is quite hard as they're not part of a component's props object. I still find them useful but I'm scared to use them and would probably rely on using a Context.

    I believe using CustomEvent even skips rendering cycles and is easier to use compared to context, but still it would be unconventional and potentially non-maintainable for me.

    • Nicola
      NicolaDec 7, 2024

      Thanks Alain for sharing your thoughts, in general in programming there is no best solution for everything, you need to find the best one for your needs and you personal developer experience. For me context are super good but I hate the doom they create when you have too many, and you need to create super contexts to avoid too complex context trees

      • Alain D'Ettorre
        Alain D'EttorreDec 7, 2024

        You're right, Contexts can get ugly fast and I honestly don't like them much, BUT they are idiomatic to React and they're guaranteed to be the go-to first-party solution for sharing state and behavior, so it's ok for me.

        About CustomEvent again, despite being great and standard, it's clearly not idiomatic in React and using a message bus that's external to fix an internal concern feels like a hack. My 2 cents

        • Nicola
          NicolaDec 7, 2024

          Yep you’re right, keep in mind that this solution starts from a react “problem” but extends to an architecture that you can use everywhere. Keep in mind that you don’t need to fully replace react standards as this system can help you in some scenarios, but not for every one. As always you need to balance solutions based on problems.

          For example, I like to use events when you open a table item details view in a modal that overlays the table, you edit the item and you need the table to refresh. In that case events are great as you can just say “an item has changed” and the table fetch data again. These two views might be not aware of each other depending on your implementation, so don’t take this approach as the best one for every case, but for some cases

  • Dusan Petkovic
    Dusan PetkovicDec 6, 2024

    I mix of the 2 would also be a good solution, I actually use an event driven approach along with normal event callbacks, so that I can easily trigger global events like notifications, errors and easily capture them, also to decouple certain components and allow them to communicate and handle logic without needing to prop drill event handles that don't belong.

    • Dusan Petkovic
      Dusan PetkovicDec 6, 2024

      in essence you have one EventProvider that you wrap your app with, then have a context that passes methods for triggering, subscribing, unsubscribing, and have a custom hook for it called
      useEvent();

      const { subscribe, trigger } = useEvent();
      
      
      trigger("post:published",arg1,arg2); 
      
      
      useEffect(() => {
      
      const handler = (arg1,arg2) => {
        console.log("do something here");
      }
      
      subscribe("post:published",handler);
      return () => unsubscribe("post:published",handler)
      
      },[subscribe])
      
      
      Enter fullscreen mode Exit fullscreen mode
  • Lucas Medina
    Lucas MedinaDec 6, 2024

    I like this approach. It's really interesting when dealing with state that's only used by a single component, but triggered from anywhere.
    Maybe a good example would be status alerts, or UI notifications, since it's commonly triggered by various places but its data is only used by one component.

  • Panda张向北
    Panda张向北Dec 6, 2024

    Many thanks. This is a great idea

  • Jack Faiz
    Jack FaizDec 6, 2024

    Using an event-driven approach sounds great—decoupling components can really help with jinx manhwa scalability and maintainability in larger apps.

  • Nicola
    NicolaDec 7, 2024

    Thanks Daniel! But this is not intended as a global state management system, but as a communication alternative to callbacks

  • KC
    KCDec 10, 2024

    If it comes to the state management side, wouldn't using useContext be a more effective approach to solve the decoupling parent and children?

    • Nicola
      NicolaDec 10, 2024

      Thanks for you comment, the event system is not intended as a state management replacement system, but as a communication between components alternative

  • Okechukwu Ifeora
    Okechukwu IfeoraDec 13, 2024

    Hey there, Great article!

    Would you be open to checking this out: npmjs.com/package/react-busser?

    It is a package based on event-driven data communication using a hook useBus(). I built it for the purposes you have listed in your wonderful article. It has some building-block hooks for lists (arrays), composites (object literal) and properties (primitive types).

    It also has other helper hooks too that are great for UI development.

    Let me know what you thick

    • Nicola
      NicolaDec 13, 2024

      Thanks dude, first of all, amazing job here!

      There are soo many things that it will require quite some time in order to analyze everything, so don't be mad if I will take some time to review it.

      Anything you would like me to check in particular? For instance code reviewing, logic reviewing or anything else?

      Keep working on this!

      • Okechukwu Ifeora
        Okechukwu IfeoraDec 14, 2024

        Thanks a lot. It's okay if you take time to review it.

        The more, the better i'd say.

        Yes, i would love that you help me with some logic reviewing specifically on 2 codesandbox code snippets that use the package (react-busser).

        This first one is an example/sample of how the package (react-busser)) works without the need to pass too many props just like in your article.

        It's a simple todo app and the way it works is depicted in this image.

        The concept of how the events work is what is known as (i coined this) CASCADE BROADCAST. There is a source (component) for the events implemented as a source hook and there is a target (component) for the events implemented as a target hook.

        The source and target hooks send and receive events respectively.

        You can play around with the simple todo app in the preview section of the codesandbox link above.

        The real logic issue comes with this second codesandbox. whose link is below this line.

        This is a simple e-commerce page that also uses the package (react-busser) but there's a bug when i try to filter the list of product items with the search bar.

        The functionality for adding a product to the cart works well.

        I would like a logic review on what you think might be the issue.

        Thanks a lot for this.

  • Nick
    NickDec 13, 2024

    .localservice@runtime.c8c182a3.js:26
    Warning: Cannot update a component (Sidebar) while rendering a different component (Body). To locate the bad setState() call inside Body, follow the stack trace as described in reactjs.org/link/setstate-in-render
    at Body (eventdrivearch-oc1y--5173--c8c182a...)
    at div
    at App

  • Rafael De Leon
    Rafael De LeonJan 3, 2025

    One reason I stopped using event driven architecture was not knowing where the event was coming from. Tracing back event originators got difficult in large applications in big teams. It's why React was created in the first place.

  • Numan Arshad
    Numan ArshadJan 3, 2025

    It's a great idea to communicate child component without the involvement of a parent, another solution is to context API but I guess for that you wrap both child parent and then can use this, using this approach you can just communicate the child component without any react dependency

    • Nicola
      NicolaJan 8, 2025

      Yep exactly, the "issue" with context is that you might need to create many of them, resulting in a doom dom tree.

      This approach works similarly, except for state management, but you don't need to create many contexts and you can communicate between dom-independent elements

  • Home Boy
    Home BoyJan 5, 2025

    Great article and examples! However, I would rather prefer keeping the event handling logic in a separate module altogether and that too inside a class and exposing its object only (as per the singleton pattern), this would mitigate the dependency of passing the dispatch callback to child components and would ensure that the event handling logic whether listening to events or trigger events is done at a single place, keeping the code relatively simpler. Would love to know your thoughts on this!

    • Nicola
      NicolaJan 8, 2025

      Thanks for the suggestion, if you have any example that would be great!

      • Home Boy
        Home BoyJan 10, 2025

        Something like this:

        class EventHandler {
            constructor() {
                this.eventStore = {};
            }
        
            on(event, callback) {
                if (!this.eventStore[event]) {
                    this.eventStore[event] = [];
                }
                this.eventStore[event].push(callback);
            }
        
            emit(event, ...args) {
                if (this.eventStore[event]) {
                    this.eventStore[event].forEach(callback => callback(...args));
                }
            }
        
            off(event, callback) {
                this.eventStore[event].splice(this.eventStore[event].indexOf(callback), 1);
            }
        };
        
        export default new EventHandler();
        
        Enter fullscreen mode Exit fullscreen mode

        Now, this can be used globally as a standalone module without re-initializing the event listener for each component. The off method can be employed at useEffect for each component for memory cleanup—just my 2 cents on this.

        • Nicola
          NicolaJan 16, 2025

          Thanks a lot, great example!

  • Zhalok Rahman
    Zhalok RahmanJan 11, 2025

    Image description

    i am a bit confused why you gave the diagram of props drilling in the even driven architecture
    the main reason why we are using event driven architecture or zustand store or even simple context is to pass data avoiding props drilling right?

    in the code as well you havent done props drilling which is obvious but in the diagram it seems the architecture involves props drilling
    I think if you change the diagram a bit it would be less confusing for anyone else

    But anyway truely appreciate your blog, event driven approach is a good architecture and you can also make it further optimized by running any heavy task on a separate event loop using worker threads in node js, i am not sure if it will work in react as well since I am saying this from nodes perspective

    • Nicola
      NicolaJan 16, 2025

      Thanks @zhalok, I will make the diagram more clear if its kinda confusing.

      Just to clarify things a little bit: this article was written to deal with callbacks drilling, and indeed is what the event system aims to.

      To solve props drilling you need to use a store management system like zustand, centralize the state and avoid passing props from root to children.

      This is not what this article aims to, but you're right I need to state it clearly.

      Regarding worker threads, they works too. We tried with them and they are a good solution to implement an event system arch as you can send messages using workers, and also handle complex stuff on a different thread.

      We decided to use window events for simplicity, and also because we didn't need such a complex management for our needs.

      But as always, there is not best choice in term of implementation, there is only the best fit for your applications needs. In our case window events were good enough, easy-to-understand solution.

  • mohit1607
    mohit1607Jan 22, 2025

    Its just new thing for me to explore,
    amazing article thank you

  • Sachin Yadav
    Sachin YadavJan 28, 2025

    Nice and clever approach!
    I feel like some folks are mistaking this for 'parent-child communication,' but it’s not that at all. This is more like a standalone broadcasting system—any part of the app can tune in if it’s interested.
    To me, it’s got that pub/sub vibe, where a component is like, 'Hey! I just did this thing—anyone care?'

  • Jakub Folejtar
    Jakub FolejtarMar 4, 2025

    You can also just use the native Context approach.
    react.dev/reference/react/createCo...
    It also helps to separate concerns.
    The context can be totally whatever.

    In your counter example, you would create a context named ClickCounter with e.g.<ClickCounter.provider value={value, increment}> wrapping the root component of where this context makes sense, where increment is just function increment() {setValue(value => ++value)} so you don't need to resolve anything inside the components using it.

    In the components, you call const {value, increment} = useContext(ClickCounter) and you are ready to go.

    If you need to decouple your component from a specific context, you can pass the context as a prop I believe and e.g. default to a useState inside the component if the context is not defined, or use it. If you really have such component though, it may make more sense to just pass the value and callback to it as typically and resolve the context in higher order component.

Add comment