question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Opt-out of cross-tab syncing?

See original GitHub issue

This library is fantastic, thank you!

I’ve come across a use-case where I need to opt-out of the cross-tab syncing, would it be possible to get a config option for this?

Here’s the part I’m talking about:

https://github.com/astoilkov/use-local-storage-state/blob/master/src/useLocalStorageStateBase.ts#L80-L93

My use-case

I’m using use-local-storage-state to track recent URL paths visited.

For example, I’ll push each path onto an array such as: ['/about', '/blog', '/contact'], etc.

I then want to update the “Recent Path” on page load, so I do it in a useEffect:

import { useEffect } from 'react'
import { createLocalStorageStateHook } from 'use-local-storage-state'

const useLocalStorage = createLocalStorageStateHook('recent-paths', [])

// `path` is derived from the current URL
export const useRecentPathsTracker = (path) => {
  const [recentPaths, setRecentPaths] = useLocalStorage()
  useEffect(() => {
    // Make sure `path` is de-duplicated and prefixed to the start of the array
    setRecentPaths([
      path,
      ...(recentPaths || []).filter(recentPath => recentPath !== path),
    ])
  }, [path, recentPaths, setRecentPaths])
}

This works fine in a single tab, but when I open 2 tabs that point to different paths (for example; example.com/blog & example.com/about), the following happens:

  1. Open example.com/blog in Tab 1
  2. The useEffect calls setRecentPaths(['/blog'])
  3. Open example.com/about in Tab 2
  4. The useEffect sets setRecentPaths(['/about', '/blog'])
  5. Calling setRecentPaths in Tab 2 updates the window’s storage item, which triggers an event in Tab 1
  6. Tab 1’s useEffect sees a new value for recentPaths, so runs again and calls setRecentPaths(['/blog', '/about'])
    • Note the blog & about have swapped
  7. Calling setRecentPaths in Tab 1 updates the window’s storage item, which triggers an event in Tab 2
  8. Tab 2’s useEffect sees a new value for recentPaths, so runs again and calls setRecentPaths(['/about', '/blog'])
    • Note the blog & about have swapped back again
  9. And so on…

I don’t actually need the cross-tab syncing for this usage, so would be happy with something like a { sync: false } option:

const useLocalStorage = createLocalStorageStateHook('recent-paths', [], { sync: false })

The workaround for now is to use the functional form of the setRecentPaths call:

export const useRecentPathsTracker = (path) => {
  const [, setRecentPaths] = useLocalStorage()
  useEffect(() => {
    // NOTE: We use the functional version of state setting here to avoid an
    // infinite loop when two tabs are open with different values for
    // `path`. This is a problem because `use-local-storage-state` will sync the
    // value across tabs by subscribing to the `session` window event.  If we
    // were to add `recentPaths` to the dependency list of `useEffect`, it would
    // trigger a re-evaluation of the effect when either tab altered value and
    // so the tabs would fight about setting their own path to be the first in
    // the list.
    // But by using the functional style, it's not a dependency, and our effect
    // is only executed when this page's `path` changes, which is what we expect
    setRecentPaths(recentPaths => {
      return [
        path,
        ...(recentPaths || []).filter(recentPath => recentPath !== path),
      ]
    })
  }, [path, setRecentPaths]) // Only run once per page load
}

The end result is the setRecentPaths is only called once per mount, which is what I expected initially.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:10 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
astoilkovcommented, Sep 21, 2021

Yep, you are right. Sorry. I might have asked too many irrelevant questions. Your use case makes sense. I will think about it more. Thanks!

1reaction
astoilkovcommented, Mar 8, 2021

Isn’t it true that if you use useRecentPathsTracker() at two places in your code the issue will appear without needing two tabs? This makes me think that the problem is not the syncing but that the useEffect() updates the value every time it changes. Using setRecentPaths(recentPaths => {}) seems like a logical solution. Another way of fixing this problem is by making the useEffect() run only on initial load by providing [] as a second parameter.

What I want to say is that it doesn’t seem to be a problem with the syncing in particular but with the way you update the values.

Also, correct me if I am wrong but the first version of your code will create an infinite loop. The reason why it isn’t blocking is that it uses useEffect(). If you change it to useLayoutEffect() it will hang your app.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Define a Cross Tab Report - Oracle Help Center
From an open report, click the Display tab. · Click Cross Tab. · Select Choose Columns. · Enter field information. · Click OK....
Read more >
Make summary data easier to read by using a crosstab query
Crosstab queries are useful for summarizing data, calculating statistics and then grouping the results.
Read more >
Letting members opt-out of surveys - QuestionPro
Members will be able to opt-out of surveys if they do not wish to partcipate.You can now turn on a setting saying "Survey...
Read more >
Use Financial Time Frames in Resource Utilization Reports ...
This setting is not relevant when Enable Resource Utilization Reporting (System setting 8.9) is disabled. Once you opt in, you will not be...
Read more >
How To Sync Basic Opt-Out Options for Microsoft Dynamics
Organizations using a marketing automation solution need to consult their marketing team to determine the type of sync required between opt-out ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found