Can two writable stores in Svelte be set up to subscribe to each other simultaneously?

There is a unique scenario where two objects share data, yet have different structures.

For instance, the 'Team' object has the team ID as its key. The 'Team' object includes 'name' and 'users' objects as its values. The 'users' object utilizes user IDs exclusive to each team.

I need to create a new object that consolidates all users from different teams.

The 'users' object should be subscribable by users, with changes reflecting in the 'Team' object as well. Similarly, the 'Team' object should be subscribable by users, with modifications propagating to the 'users' object.

How can I accomplish this task?

I tried updating both objects using a 'subscribe' function in JavaScript files, but encountered an infinite loop and failure.

Below is the example code along with REPL.

<script>
    import {writable} from "svelte/store";
    
    const teamDict = writable({})
    const userDict = writable({})

    function initTeamDict() {
        teamDict.set({
            1: {
                name: "good team",
                users: {
                    1: "James",
                    2: "Poppy",
                    48: "Hway"
                }
            },
            2: {
                name: "bad team",
                users: {
                    47: "Kelin",
                    35: "Teo",
                    24: "Ferma"
                }
            }
        })
    }

    function initUserDict() {        
        userDict.set(Object.values($teamDict).reduce((acc, team) => ({...acc, ...team[`users`]}), {}))
    }


</script>

<button on:click={initTeamDict}>init team dict</button>
<button on:click={initUserDict}>init user dict</button>

<div> {JSON.stringify($teamDict)}</div>
<div> {JSON.stringify($userDict)}</div>

<button on:click={() => $teamDict[`1`][`users`][`1`] = "top"}>this button should change userDict also </button>
<button on:click={() => $userDict[`1`] = "bottom"}>this button should change teamDict also </button>

REPL

Edit

By following @ghostmodd's solution, I resolved the issue with the provided code below.

Since modifying a copied object does not trigger a subscription, I duplicated the object before making changes.

In order to manage the modified object rendering order, I implemented a separate view using derived.

<script>
    import {derived, writable} from "svelte/store";
    import {Button} from "flowbite-svelte";

    const teamDict = writable({})
    const userDict = writable({})

    teamDict.subscribe(($$teamDict) => {
        const userDictCopy = $userDict
        for (const key in userDictCopy) {
            delete userDictCopy[key]
        }
        Object.assign(userDictCopy, Object.values($$teamDict).reduce((acc, team) => ({...acc, ...team[`users`]}), {}))
    })
    userDict.subscribe(($$userDict) => {
        const teamDictCopy = $teamDict
        for (const team of Object.values(teamDictCopy)) {
            team[`users`] = {}
        }
        for (const [userId, user] of Object.entries($$userDict)) {
            teamDictCopy[user[`team_id`]][`users`][userId] = user
        }
    })

    const storeView = derived(
        [teamDict, userDict],
        ([$teamDict, $userDict], set) => {
            set({teamDict: $teamDict, userDict: $userDict})
        }
    )

    function initTeamDict() {
        teamDict.set({
            1: {
                name: "good team",
                users: {
                    1: {
                        "name": "James",
                        "team_id": 1
                    },
                    2: {
                        "name": "Poppy",
                        "team_id": 1
                    },
                    48: {
                        "name": "Hway",
                        "team_id": 1
                    }
                }
            },
            2: {
                name: "bad team",
                users: {
                    47: {
                        "name": "Kelin",
                        "team_id": 2
                    },
                    35: {
                        "name": "Teo",
                        "team_id": 2
                    },
                    24: {
                        "name": "Ferma",
                        "team_id": 2
                    }
                }
            }
        })
    }


</script>

<Button on:click={initTeamDict}>init team dict</Button>

<div> {JSON.stringify($storeView.teamDict)}</div>
<div> {JSON.stringify($storeView.userDict)}</div>

<Button on:click={() => $teamDict[`1`][`users`][`1`][`name`] = "top"}>this button should change userDict also </Button>
<Button on:click={() => $userDict[`1`][`name`] = "bottom"}>this button should change teamDict also </Button>

REPL

Answer №1

If you're looking to achieve this functionality, you can utilize an intermediary store in Svelte called a "derived store." This concept is akin to React's useEffect hook, as it actively watches specified stores for updates and manages them accordingly.

In addition to your existing "Teams" and "Users" stores, consider creating a new derived store that will effectively capture and store any updates made.

Here's a simplified breakdown of how I approached this task:

const teamStore = writable({})
const userStore = writable({})

Next, I set up the intermediate derived store to track changes in the previously defined stores.

const intermediaryStore = derived(
    [teamStore, userStore],
    ([$teamStore, $userStore], set) => {
        // Your logic goes here
    },
    {
        teamStore: {},
        userStore: {},
    }
)

It's crucial to understand the function arguments - the array of observable stores, the update handler function, and the initial value of the derived store.

The final step involves populating the derived store with the necessary data by implementing a custom handler function.

// Create a copy of the teamStore for easier manipulation
const teamCopy = {
    ...$teamStore,
}

for (let userID in JSON.parse(JSON.stringify($userStore))) {
    const teamID = $teamStore[$userStore[userID].team]

    if ($teamStore[teamID]) {
        teamCopy[teamID].users[userID] = $userStore[userID]
    } else {
        console.log("Error! Team not initialized.")
        return
    }
}

const userCopy = Object.values($teamStore).reduce((acc, team) => ({...acc, ...team[`users`]}), {})

set({
    teamStore: teamCopy,
    userStore: userCopy,
})

Check out the live demo on REPL: Live Demo

P.S. Please excuse any language mistakes - I'm still polishing my English skills! 😊

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to save the content of an RFC822 message body as a String?

let inbox = require("inbox"); let client = inbox.createConnection(false, "imap.gmail.com", { secureConnection: true, auth:{ user: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="99f4e0fcf4f8f0f5d9fef4f8f0f5b7fa ...

If the visitor navigated from a different page within the site, then take one course of action; otherwise

How can I achieve the following scenario: There are two pages on a website; Parent page and Inside page. If a user navigates directly to the Inside page by entering the URL or clicking a link from a page other than the Parent page, display "foo". However, ...

Implement pop-up functionality on additional buttons. Modify existing code to accommodate multiple buttons

I have a feature that allows me to click on a button and trigger a pop-up window with the desired content. The issue I am facing is how to duplicate this functionality for multiple buttons, each triggering a different pop-up content. I attempted to duplic ...

Leveraging the On Keyup Function in Real-Time

How can I modify this code to run after an ajax call using .live method? var element=document.getElementById('txt_url'); element.onkeyup=function(){ var input=element.value; if(input=='') return; if(input.indexOf('h ...

Linkyfy.js does not function correctly with each and not statements

Trying to incorporate a linkifying script on a website, which transforms URLs in text into clickable links. Utilizing the following solution: https://github.com/SoapBox/linkifyjs To make it operational (post-download), the following steps are required: & ...

Error: Angular router outlet could not find any matching routes for the requested

I have been working on an application that utilizes lazy-loaded modules for each main section of the app. In one module, I have two router outlets - a primary one and one called details. const routes: Routes = [ { path: '', component: BooksCo ...

The authentication protocol, Next Auth, consistently provides a 200 status response for the signIn function

I ran into a challenge while building my custom login page using Next Auth. The issue arises when I try to handle incorrect login data. If the login credentials are correct, I am able to successfully send JWT and redirect to /dashboard. However, when the l ...

Socket.io-powered notification system

I'm currently in the process of developing a notification system for my Events Manager Website. Every time a user is logged in and performs an action (such as creating an event), a notification about the event creation should be sent to other user ...

Guide on invoking the callback function using the class name specified in a different JavaScript file

After attempting to invoke the callback function using a classname defined in another JavaScript file, I encountered difficulties and was unable to make the call successfully. Despite my efforts, I couldn't pinpoint the mistake I made. Kindly review t ...

Error thrown: Upon attempting to reopen the modalbox after closing it, an uncaught TypeError is encountered, indicating that the function $(...).load

An unexpected error occurred: $(...).load(...).modal is not functioning properly After closing a modal, I encountered this error in the console when attempting to reopen it. Strangely, it seems to work intermittently for a few times before throwing this e ...

Information submitted through an ajax request does not get saved in the $_POST array

After successfully executing an AJAX request using GET, I decided to try POST this time. However, when attempting to send data, a baffling error message appeared in the console - NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED: 'JavaScript component does ...

Issues encountered with the functionality of face-api and tensorflow.js within the browser

I've been trying to execute this example in the browser Check out the example here Specifically looking at this code snippet <!DOCTYPE html> <html> ... (Contents of the code snippet) ... </body> </html> Unfortunately, I&apos ...

Preventing Broken URLs in Jquery each

What is the best way to prevent taking images with broken URLs? jQuery.each(jQuery('img'), function(index, obj) { imageStack.add(jQuery(obj)); }); ...

What is the best method to override the CSS transition effect with JavaScript?

I am trying to implement a CSS transition where I need to reset the width of my box in advance every time my function is called. Simply setting the width of the box to zero makes the element shrink with animation, but I want to reset it without any animati ...

Using Jquery to handle input data in a form

Using jQuery, I have set up an AJAX function to fetch data from a database in real-time as the user fills out a form with 5 input fields. Currently, my code looks like this: $("#searchtype, #searchtext, #searchtag, #daterangefrom, #daterangeto").on("load ...

The checkbox is failing to display as checked even after its value has been dynamically set to true

Currently, I am immersed in a project within ASP.NET MVC that involves displaying data on a page where users can interact by selecting checkboxes to make changes. In cases where there are numerous checkboxes present, a "Select all Checkboxes" button become ...

What is the best way to create a function that will return a Promise within an Express Route?

I am working with a business level database module named "db_location" that utilizes the node-fetch module to retrieve data from a remote server through REST API. **db_location.js** DB LOGIC const p_conf = require('../parse_config'); const db_ ...

Deciphering a JSON Array in JavaScript to extract specific components

I have a brief snippet for a JSON array and a JavaScript function that currently returns a single argument: <!DOCTYPE html> <html> <body> <h2>JSON Array Test</h2> <p id="outputid"></p> <script> var arrayi ...

Issue with React container not connecting properly

I am currently facing an issue with a search bar where the input value is not displaying properly. Even when I check the value in the console, it only shows one character at a time. https://i.stack.imgur.com/Qm0Lt.png My assumption is that there might be ...

The technique of binding methods in React

When working with React.js, it's recommended to define your method binding in the constructor for better performance. Here's an example: constructor(props){ this.someFunction = this.someFunction.bind(this); } This approach is more efficient t ...