Simplify State Syncing in React with React State Sync

Using React State Sync to simplify state syncing in React

Sunday, 21 May 2023

State management is a crucial aspect of building robust and interactive applications with React. It allows developers to store and manage data within components, enabling them to create dynamic user interfaces. However, as applications grow in complexity, managing state across multiple components, apps, or even different tabs can become challenging.

There are plenty of existing solutions to handle state management in React, such as Redux, MobX, and React Context. But these solutions can be overkill for simple use cases, and they don't provide a straightforward way to share state between different components or React apps on a page.

Having come across this problem myself, I put together this simple hook which can handle simple state syncing across various components within the same app, between different apps, or even across different browser tabs. By leveraging the BroadcastChannel API, React State Sync enables seamless synchronization of state changes between different instances of your React application.

The Power of State Syncing

State syncing in React can be incredibly useful in various scenarios. Let's consider a common use case: an application with multiple components that need to access and update a shared piece of data. Traditionally, developers would need to lift the state up to a common ancestor component and pass it down as props to all the components that require it. This can lead to prop drilling, making the codebase harder to maintain and understand.

Other state libraries like Redux and MobX can help solve this problem by providing a global state store that can be accessed by any component. However, these libraries can be overkill for simple use cases, and they don't provide a straightforward way to share state between different components or React apps on a page, since they need to use a provider component to wrap the entire application.

React State Sync offers a simple solution by allowing components to access and update shared state directly, regardless of their position in the component tree. It eliminates the need for prop drilling and uses a browser native API.

Island Architecture and Tab Syncing

One of the significant advantages of React State Sync is its support for an island architecture. In this context, an island architecture refers to a scenario where multiple React applications coexist on a single page or within a single project. Each application, or "island," operates independently and manages its own state. React State Sync enables these islands to communicate and synchronize state changes seamlessly.

Furthermore, React State Sync goes beyond application boundaries by supporting state syncing across different tabs within the same browser. When a change occurs in one tab, React State Sync ensures that the updated state propagates to other open tabs, allowing for real-time synchronization of data. This feature is particularly useful when dealing with collaborative applications or scenarios where multiple instances of the same application are opened across different tabs.

Use Cases

React State Sync offers a wide range of use cases where state syncing can simplify development and enhance user experiences. Here are a few examples:

1. Shared Data between Components

Consider a scenario where multiple components in your application need access to a shared dataset. Instead of fetching the data independently in each component, React State Sync allows you to fetch the data once and then sync it across all components that require it. This approach eliminates redundant API requests, improves performance, and ensures consistent data across the application.

import {useSyncedState} from "react-state-sync";

const App = () => {
    const [data, syncData] = useSyncedState("sharedData", null);

    useEffect(() => {
        // Fetch data from an API
        fetchData().then((response) => {
            syncData(response.data);
        });
    }, []);

    return (
        <div>
            <ComponentA/>
            <ComponentB/>
            <ComponentC/>
        </div>
    );
};

const ComponentA = () => {
    const [data] = useSyncedState("sharedData", null);

    return (
        <div>
            <p>Component A</p>
            <p>Data: {data}</p>
        </div>
    );
};

// Etc...

2. Optimistic Updates with Rollback

React State Sync also provides support for optimistic updates, a technique commonly used in scenarios involving asynchronous operations, such as API requests. With optimistic updates, you can immediately update the state optimistically before the operation completes, providing a responsive user experience. If the operation fails, you can roll back the state to its previous value.

import {useSyncedState} from "react-state-sync";

const App = () => {
    const [count, syncCount, updateCountOptimistically] = useSyncedState("count", 0);

    const handleIncrement = async () => {
        const {rollbackValue, syncValue} = updateCountOptimistically(count + 1);

        try {
            // Perform API request or some operation
            await incrementCountAPI();

            // Sync the new value across tabs
            syncValue();
        } catch (error) {
            // Rollback to the old value in case of failure
            rollbackValue();
        }
    };

    return (
        <div>
            <button onClick={handleIncrement}>Increment</button>
            <p>Count: {count}</p>
        </div>
    );
};

3. Inter-App Communication

React State Sync not only allows for state synchronization within a single application but also facilitates communication between different React applications. This can be incredibly useful when building modular and micro-frontend architectures, where different applications need to exchange data and stay in sync.

// App 1
import {useSyncedState} from "react-state-sync";

const App1 = () => {
    const [message, syncMessage] = useSyncedState("message", "");

    const handleMessageChange = (event) => {
        syncMessage(event.target.value);
    };

    return (
        <div>
            <input type="text" value={message} onChange={handleMessageChange}/>
        </div>
    );
};

// App 2
import {useSyncedState} from "react-state-sync";

const App2 = () => {
    const [message, syncMessage] = useSyncedState("message", "");

    return (
        <div>
            <p>Message from App 1: {message}</p>
            <button onClick={() => syncMessage("Hello from App 2")}>
                Send Message
            </button>
        </div>
    );
};

In this example, App1 and App2 are separate React applications residing on the same page. They can communicate with each other by sharing state through React State Sync.

Conclusion

React State Sync simplifies state syncing in React applications, enabling seamless communication and synchronization between components, applications, and browser tabs. By leveraging the BroadcastChannel API, this library provides an elegant solution for sharing and managing state across different contexts, eliminating the need for complex state management patterns and reducing the burden of data synchronization.

Whether you need to share data between components, perform optimistic updates, or enable inter-app communication, React State Sync empowers you to build more efficient, scalable, and collaborative React applications. Give it a try let me know what you think!

To learn more about React State Sync and explore its features and usage, visit the official GitHub repository.