facebookexperimental / Recoil

Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.
https://recoiljs.org/
MIT License
19.51k stars 1.18k forks source link

[SSR][NextJS] Duplicate atom key during development and during production build in nextjs #733

Closed janus-reith closed 1 year ago

janus-reith commented 3 years ago

Both during local development and also when building a production build, I’m getting a duplicate atom key for each key and each page using it after the first one.

I put together a quick sandbox to demonstrate the issue: https://codesandbox.io/s/flamboyant-sea-tqlky

The errors show up in the build log, a quick way to test is using the integrated vercel deployment button. Looking at the default nextjs example, I can‘t spot any special settings there that would prevent whatever duplication is going on from happening: https://github.com/vercel/next.js/tree/canary/examples/with-recoil That example however only makes use of recoil state on one page.

alexilyaev commented 3 years ago

Also mentioned in:
https://github.com/facebookexperimental/Recoil/issues/213#issuecomment-659349900

This is how it looks like in the Vercel deployment build: image

leonardomgt commented 3 years ago

Has this been solved? I'm having the same issue.

janus-reith commented 3 years ago

@leonardomgt I was not able to solve this yet.

@drarmstr Tagging you as you seem to be a main contributor to this project, any chance you could have look at this? Thanks!

drarmstr commented 3 years ago

@janus-reith - Sorry, I'm not very familiar with Next.JS or SSR.

alexilyaev commented 3 years ago

@janus-reith - Sorry, I'm not very familiar with Next.JS or SSR.

Next.js has a concept of Pages. Imagine an SPA with multiple entry points, like an Admin and a Client App, both importing a module that declares a Recoil atom. So it doesn't have to be Next.js related.
Essentially it's a single Node.js process that executes JS files (build process) that eventually declare the same atom several times.

In development, when a file is changed, Next.js re-builds the relevant page entry file.
Because it's the same Node.js process, the atom has already been declared.
The same thing can happen with HMR when the file change triggers a rebuild of the whole file, or even when the atom is declared inside a component lifecycle/hook and only that is being hot-replaced.

Basically, I can't think of a solution to this problem besides providing a configuration setting to disable this check/warning.
At a minimum, developers should be able to disable it.
In Next.js or SSR that would be when running on the server.
In HMR, that would be when not in production env.

A zero config alternative might be tempting. E.g. relying on NODE_ENV=production like React does for it's optimizations.
It would probably solve the HMR issue, but not the build of Next.js, as that runs with NODE_ENV=production obviously.
Although, that would already be better than what we currently have.

drarmstr commented 3 years ago

Related to #247

janus-reith commented 3 years ago

Thanks @drarmstr for the response nonetheless, and thanks @alexilyaev for the detailed description.

Regarding the multiple entry points, I wonder if this could be worked around by some more advanced tracing logic to distinguish actual redeclaration of the key from the same code being executed again using the callsite? I guess that depending on the way atoms are initialized there could be cases where this is not fully reliable(e.g. some dynamic declaration with variable-based key names ), also I'm not sure about browser support but still, maybe that is a feasible approach to consider.

Him-2C commented 3 years ago

Me to for this issue.

juanpprieto commented 3 years ago

Hey guys, any update on this issue? Has anybody come up with a workaround? Thanks!

juanpprieto commented 3 years ago

Ok guys, here's a temporary "solution" via a webpack "plugin" that kills the Duplicate Warnings by literally modifying the recoil source. sadness

from...

function registerNode(node) {
  if (nodes.has(node.key)) {
    ....code
    console.warn(message); // @oss-only
  }
  ....code
}

to:

function registerNode(node) {
  if (nodes.has(node.key)) {
    ....code
    (!process.env.__NEXT_PROCESSED_ENV && !process.env.SILENCE_RECOIL_DUPE_WARNING) && console.warn(message); // @oss-only
  }
  ....code
}

✅ Am I desperate? ✅ Is this ugly? ✅ is it a huge hack? ✅ Works? tested in next@^10.0.3 / recoil@^0.1.2 with SILENCE_RECOIL_DUPE_WARNING=true ✅ Could work outside nextjs?. Maybe.. SILENCE_RECOIL_DUPE_WARNING=true ✅ Works in netlify ? ⚠️ Is this a joke? 🚫 Fully tested (not even close) 🤷‍♂️ Its safe to use in production

// feel free to replace these
const fs = require('promise-fs')
const path = require('path')

/**
 Webpack plugin that modifies /node_modules/recoil/[...]/recoil.js
 adding process.env conditionals before the registerNode/duplicate key warning log
**/
{
  apply: (compiler) => {
    return compiler.hooks.run.tapAsync(
      "SilenceRecoilDupeWarningBeforeCompile",
      async (compilation, callback) => {
        const SILENCING_VARS = [
          "__NEXT_PROCESSED_ENV",
          "SILENCE_RECOIL_DUPE_WARNING",
        ];

        const silenceModule = async (module) => {
          // target recoil module & path (Adjust for your own project) 👈 mono repo setup
          const recoilModulePath = path.join(
            process.cwd(),
            "..",
            `/node_modules/${module}`
          );

          console.log(`Disabling ${recoilModulePath} warnings`);

          // read the source module
          const recoilSource = await fs.readFile(recoilModulePath, "utf8");

          // any of these will silence the warning
          const conditionalString = `(${SILENCING_VARS.reduce(
            (condStr, envVar) => {
              condStr = [...condStr, `!process.env.${envVar}`];
              return condStr;
            },
            []
          ).join(" && ")}) &&`;

          // regex and replace function
          const duplicateWarningRegex = /(if \(nodes\.has\(node\.key\)\) \{.*?)(console\.warn\(message\)\;)(.*?\})/gs;

          const duplicateWarningReplace = (str, $1, $2, $3) => {
            if ($1 && $3) {
              return `${
                $1 ? $1.replace(conditionalString, "") : ""
              }${conditionalString} console.warn(message);${$3}`;
            }
            return str;
          };

          // modify the recoil source file
          const modifiedRecoilSource = recoilSource.replace(
            duplicateWarningRegex,
            duplicateWarningReplace
          );

          // overwrite the recoil module - cause you can 🤷‍♂️
          await fs.writeFile(recoilModulePath, modifiedRecoilSource, "utf8");
        };

        try {
          const TARGET_RECOIL_MODULES = [
            "recoil/cjs/recoil.js",
            "recoil/es/recoil.js",
            "recoil/umd/recoil.js",
          ];

          for (let m = 0; m < TARGET_RECOIL_MODULES.length; m++) {
            const currentModule = TARGET_RECOIL_MODULES[m];
            await silenceModule(currentModule);
          }

          console.log("Disabled recoil duplicate key warning (hopefully).");
          callback();
        } catch (error) {
          console.log("SilenceRecoilDupeWarningBeforeCompile", error);
          callback();
        }
      }
    );
  };
}

Hopefully this enthusiasm sparks a proper solution... 👍

UPDATE: less invasive solution for nextjs here

adamhenson commented 3 years ago

I am reproducing this on my Next.js app. I only do SSR when running locally in dev mode, otherwise in production - I export static files via SSG. From what I can tell, this is just a warning and safe to ignore. Even though I'm a newbie to Recoil - it seems to be working 😨? I actually only see the output in the server logs... not the browser. Although the output is annoying, I just want to make sure this isn't a blocker in using Recoil. Can I just ignore the output? Or is this issue about actual broken functionality?

juanpprieto commented 3 years ago

I am reproducing this on my Next.js app. I only do SSR when running locally in dev mode, otherwise in production - I export static files via SSG. From what I can tell, this is just a warning and safe to ignore. Even though I'm a newbie to Recoil - it seems to be working 😨? I actually only see the output in the server logs... not the browser. Although the output is annoying, I just want to make sure this isn't a blocker in using Recoil. Can I just ignore the output? Or is this issue about actual broken functionality?

You can ignore the warning output (if you can bare it), functionality is not affected (We have a few sites in production working just fine) :(

R-Bower commented 2 years ago

I'm very eager to start using Recoil in my applications, but errors like this make me wary of adopting. Any official update on this? I'd prefer a solution that doesn't involve modifying the source code in node_modules.

juanpprieto commented 2 years ago

I feel you, I love recoil too much (specially on Nextjs), so I've taken a leap of faith that the staff will do something about it since SSR environments are here to stay and we have very complex production apps running beautifully with it. Fingers crossed 🙏🏼

R-Bower commented 2 years ago

@juanpprieto I've found a less invasive method that relies on the intercept-stdout package.

Add the following to next.config.js (outside of the exported configuration):

const intercept = require("intercept-stdout")

// safely ignore recoil warning messages in dev (triggered by HMR)
function interceptStdout(text) {
  if (text.includes("Duplicate atom key")) {
    return ""
  }
  return text
}

if (process.env.NODE_ENV === "development") {
  intercept(interceptStdout)
}

This way I don't have to modify the source code in node_modules. Do you see any downsides to this approach?

juanpprieto commented 2 years ago

@juanpprieto I've found a less invasive method that relies on the intercept-stdout package.

Add the following to next.config.js (outside of the exported configuration):

const intercept = require("intercept-stdout")

// safely ignore recoil warning messages in dev (triggered by HMR)
function interceptStdout(text) {
  if (text.includes("Duplicate atom key")) {
    return ""
  }
  return text
}

if (process.env.NODE_ENV === "development") {
  intercept(interceptStdout)
}

This way I don't have to modify the source code in node_modules. Do you see any downsides to this approach?

Hey @R-Bower!

On paper this seems 1000 times less invasive than my hack!

I will test at my end and let you know! — Thanks for the heads up!

R-Bower commented 2 years ago

The error still shows on in-browser hot reloads. Not sure if there's a way to suppress those other than the chrome console regex filter.

juanpprieto commented 2 years ago

The error still shows on in-browser hot reloads. Not sure if there's a way to suppress those other than the chrome console regex filter.

Yes, intercept-stdout works well for stdout suppressing in both development and production. Unfortunately, browser warnings are still there. I tried using webpack native stats suppressing and webpack-filter-warnings-plugin, but those two fail to mute recoil warning for some reason, even though it does block other warnings I wanted to mute such as Critical dependency: the request of a dependency is an expression.

This why I ended up doing the hack I did unfortunately. If anyone knows another option to try for muting browser warnings I'd love to hear and try them. Thanks!

juanpprieto commented 2 years ago

@R-Bower think i may have found a cleaner way to kill recoil browser warnings.

src/pages/_app.jsx — Intercept client/browser warnings. Outside the exported react component

const memoize = (fn) => {
  let cache = {};
  return (...args) => {
    let n = args[0];
    if (n in cache) {
      return cache[n];
    }
    else {
      let result = fn(n);
      cache[n] = result;
      return result;
    }
  }
}

// ignore in-browser next/js recoil warnings until its fixed.
const mutedConsole = memoize((console) => ({
  ...console,
  warn: (...args) => args[0].includes('Duplicate atom key')
    ? null
    : console.warn(...args)
}))

global.console = mutedConsole(global.console);

next.config.js — Intercept stdout warnings. Outside the exported webpack config

const intercept = require("intercept-stdout")

// safely ignore recoil stdout warning messages 
function interceptStdout(text) {
  if (text.includes('Duplicate atom key')) {
    return ''
  }
  return text
}

// Intercept in dev and prod
intercept(interceptStdout)

Let me know if this works for you!

hsh2001 commented 2 years ago

@juanpprieto It works! Thanks :)

spookyvert commented 2 years ago

@R-Bower think i may have found a cleaner way to kill recoil browser warnings.

src/pages/_app.jsx — Intercept client/browser warnings. Outside the exported react component

// ignore in-browser next/js recoil warnings until its fixed.
const mutedConsole = memoize((console) => ({
  ...console,
  warn: (...args) => args[0].includes('Duplicate atom key')
    ? null
    : console.warn(...args)
}))
global.console = mutedConsole(global.console);

next.config.js — Intercept stdout warnings. Outside the exported webpack config

const intercept = require("intercept-stdout")

// safely ignore recoil stdout warning messages 
function interceptStdout(text) {
  if (text.includes('Duplicate atom key')) {
    return ''
  }
  return text
}

// Intercept in dev and prod
intercept(interceptStdout)

Let me know if this works for you!

This hangs when I start the server on next.js

Nabin0433 commented 2 years ago

Duplicate atom key "userAtomState". This is a FATAL ERROR in production. But it is safe to ignore this warning if it occurred because of hot module replacement.

TofuTseng commented 2 years ago

@R-Bower think i may have found a cleaner way to kill recoil browser warnings. src/pages/_app.jsx — Intercept client/browser warnings. Outside the exported react component

// ignore in-browser next/js recoil warnings until its fixed.
const mutedConsole = memoize((console) => ({
  ...console,
  warn: (...args) => args[0].includes('Duplicate atom key')
    ? null
    : console.warn(...args)
}))
global.console = mutedConsole(global.console);

next.config.js — Intercept stdout warnings. Outside the exported webpack config

const intercept = require("intercept-stdout")

// safely ignore recoil stdout warning messages 
function interceptStdout(text) {
  if (text.includes('Duplicate atom key')) {
    return ''
  }
  return text
}

// Intercept in dev and prod
intercept(interceptStdout)

Let me know if this works for you!

This hangs when I start the server on next.js

Me too. In terminal will show compiled successfully, but browser will always loading.

johackim commented 2 years ago

Any news about this ?

Here my temporary small fix :

// next.config.js
const intercept = require('intercept-stdout');

intercept((text) => (text.includes('Duplicate atom key') ? '' : text));
junhoyeo commented 2 years ago

I published next-intercept-stdout that wraps intercept-stdout as the form of the Next.js plugin.

  1. I didn't want to call a function separate from other NextConfig values and plugins. It makes the action look suspicious and less coupled.
  2. In addition, intercept-stdout was last distributed about six years ago, so it seemed necessary to cover the implementation separately in case it no longer works.

https://github.com/junhoyeo/next-intercept-stdout

// Example for Recoil
// next.config.js
const withInterceptStdout = require('next-intercept-stdout');
const withSvgr = require('@newhighsco/next-plugin-svgr');

module.exports = withInterceptStdout(
  withSvgr({
    reactStrictMode: true,
    svgrOptions: {},
  }),
  (text) => (text.includes('Duplicate atom key') ? '' : text),
);
keinn51 commented 2 years ago

Not for Next.js, I'm in react with Node.js. In my case, I solved this issue by using a pacakge called uuid, which generates random numbers in Node.js. I think having the same key in recoil doesn't mean having the same recoil state value. Each time re-rendering occurs, the recoil state value changes, and a different key is required each time.

You can use it as follows

import { atom } from 'recoil';
import { v1 } from 'uuid';

function Fname(name) {
  return atom({
    key: `${name}/${v1()}`,
    default: '',
  });
}

export { Fname };

Additionally I got a name from each component using recoil value. If unnecessary, ${v1()} will be ok.

ryanwellsdotcom commented 2 years ago

It seem like this would be addressed by now. Getting this in Stackblitz and other environments. Not using Next.js.

tpatalas commented 2 years ago

Yeah this is not only due to Next.js's SSR. I am currently implementing CSR with Next.js and still shows the duplication message. I dont think this is Next.js's issue at all.

This only happens during dev environment though. Although not causing any issue, it is the message for us to freak out.

royz commented 2 years ago

@R-Bower think i may have found a cleaner way to kill recoil browser warnings. src/pages/_app.jsx — Intercept client/browser warnings. Outside the exported react component

// ignore in-browser next/js recoil warnings until its fixed.
const mutedConsole = memoize((console) => ({
  ...console,
  warn: (...args) => args[0].includes('Duplicate atom key')
    ? null
    : console.warn(...args)
}))
global.console = mutedConsole(global.console);

next.config.js — Intercept stdout warnings. Outside the exported webpack config

const intercept = require("intercept-stdout")

// safely ignore recoil stdout warning messages 
function interceptStdout(text) {
  if (text.includes('Duplicate atom key')) {
    return ''
  }
  return text
}

// Intercept in dev and prod
intercept(interceptStdout)

Let me know if this works for you!

This hangs when I start the server on next.js

Worked wonderfully, thanks!

juanpprieto commented 2 years ago

Updated working version for "recoil": "^0.7.2",

Tested and working on @remix

app/entry.server.jsx

import intercept from 'intercept-stdout';

// Silence Recoil duplicate atom errors (stdout/server)
function muteStdout(log) {
  if (log.includes('Expectation Violation:')) {
    return '';
  }
  return log;
}

intercept(muteStdout, muteStdout);

app/entry.client.jsx

const memoize = (fn) => {
  let cache = {};
  return (...args) => {
    let n = args[0];
    if (n in cache) {
      return cache[n];
    }
    else {
      let result = fn(n);
      cache[n] = result;
      return result;
    }
  }
}

// Silence Recoil duplicate atom errors (client/browser)
const mutedConsole = memoize((console) => ({
  ...console,
  error: (...args) => {
    return args[0]?.name === 'Expectation Violation'
      ? null
      : console.error(...args)
  }
}))

// Overload before hydrate 
window.console = mutedConsole(window.console)

hydrate(<RemixBrowser />, document);
DaviCaamano commented 2 years ago

Has this seriously been going on for more than a year? Is recoil just not compatible with Next?

tpatalas commented 2 years ago

Completely forgot about this after suppressing the message within Next.js's config file. It works great without issues so far.

Still It is great if we can use Recoil without such messages.

juanpprieto commented 2 years ago

Yes long time and no official fix, but these warnings are not an issue in production. I have launched multiples sites in with it with complex recoil state and all are working fine. I think (maybe wrong) these false/positive warnings are caused by recoil lack of awareness of it being run on SSR/SSG runtimes (stdout warnings) and sometimes hot-module reloads in dev mode (browser)

saltycrane commented 2 years ago

Another workaround is to use patch-package (Dan Abramov likes it) to disable the warning in the Recoil source. Note: if there actually is a duplicate atom key, the warning will still show up in production.

Patch:

diff --git a/node_modules/recoil/cjs/recoil.js b/node_modules/recoil/cjs/recoil.js
index 5403308..9089ec8 100644
--- a/node_modules/recoil/cjs/recoil.js
+++ b/node_modules/recoil/cjs/recoil.js
@@ -467,7 +467,7 @@ function reactMode() {
 function isFastRefreshEnabled() {
   // @fb-only: const {isAcceptingUpdate} = require('__debug');
   // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate();
-  return false; // @oss-only
+  return true; // @oss-only
 }

 var Recoil_ReactMode = {
diff --git a/node_modules/recoil/es/recoil.js b/node_modules/recoil/es/recoil.js
index b3cf729..382768e 100644
--- a/node_modules/recoil/es/recoil.js
+++ b/node_modules/recoil/es/recoil.js
@@ -461,7 +461,7 @@ function reactMode() {
 function isFastRefreshEnabled() {
   // @fb-only: const {isAcceptingUpdate} = require('__debug');
   // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate();
-  return false; // @oss-only
+  return true; // @oss-only
 }

 var Recoil_ReactMode = {
diff --git a/node_modules/recoil/native/recoil.js b/node_modules/recoil/native/recoil.js
index 3be4f82..b645dc9 100644
--- a/node_modules/recoil/native/recoil.js
+++ b/node_modules/recoil/native/recoil.js
@@ -461,7 +461,7 @@ function reactMode() {
 function isFastRefreshEnabled() {
   // @fb-only: const {isAcceptingUpdate} = require('__debug');
   // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate();
-  return false; // @oss-only
+  return true; // @oss-only
 }

 var Recoil_ReactMode = {
diff --git a/node_modules/recoil/umd/recoil.js b/node_modules/recoil/umd/recoil.js
index 914392c..e2f3281 100644
--- a/node_modules/recoil/umd/recoil.js
+++ b/node_modules/recoil/umd/recoil.js
@@ -467,7 +467,7 @@
   function isFastRefreshEnabled() {
     // @fb-only: const {isAcceptingUpdate} = require('__debug');
     // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate();
-    return false; // @oss-only
+    return true; // @oss-only
   }

   var Recoil_ReactMode = {
codefln commented 2 years ago

Another workaround is to use patch-package (Dan Abramov likes it) to disable the warning in the Recoil source. Note: if there actually is a duplicate atom key, the warning will still show up in production.

  • add "postinstall": "patch-package" to the "scripts" section of package.json
  • npm install patch-package
  • in node_modules/recoil, edit cjs/recoi.js, es/recoil.js, native/recoil.js, and umd/recoil.js as shown in the patch below. (Maybe only the cjs and es changes are needed.)
  • npx patch-package recoil
  • git add patches
  • git commit -m "disable 'Duplicate atom key' warning in dev"

Patch:

diff --git a/node_modules/recoil/cjs/recoil.js b/node_modules/recoil/cjs/recoil.js
index 5403308..9089ec8 100644
--- a/node_modules/recoil/cjs/recoil.js
+++ b/node_modules/recoil/cjs/recoil.js
@@ -467,7 +467,7 @@ function reactMode() {
 function isFastRefreshEnabled() {
   // @fb-only: const {isAcceptingUpdate} = require('__debug');
   // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate();
-  return false; // @oss-only
+  return true; // @oss-only
 }

 var Recoil_ReactMode = {
diff --git a/node_modules/recoil/es/recoil.js b/node_modules/recoil/es/recoil.js
index b3cf729..382768e 100644
--- a/node_modules/recoil/es/recoil.js
+++ b/node_modules/recoil/es/recoil.js
@@ -461,7 +461,7 @@ function reactMode() {
 function isFastRefreshEnabled() {
   // @fb-only: const {isAcceptingUpdate} = require('__debug');
   // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate();
-  return false; // @oss-only
+  return true; // @oss-only
 }

 var Recoil_ReactMode = {
diff --git a/node_modules/recoil/native/recoil.js b/node_modules/recoil/native/recoil.js
index 3be4f82..b645dc9 100644
--- a/node_modules/recoil/native/recoil.js
+++ b/node_modules/recoil/native/recoil.js
@@ -461,7 +461,7 @@ function reactMode() {
 function isFastRefreshEnabled() {
   // @fb-only: const {isAcceptingUpdate} = require('__debug');
   // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate();
-  return false; // @oss-only
+  return true; // @oss-only
 }

 var Recoil_ReactMode = {
diff --git a/node_modules/recoil/umd/recoil.js b/node_modules/recoil/umd/recoil.js
index 914392c..e2f3281 100644
--- a/node_modules/recoil/umd/recoil.js
+++ b/node_modules/recoil/umd/recoil.js
@@ -467,7 +467,7 @@
   function isFastRefreshEnabled() {
     // @fb-only: const {isAcceptingUpdate} = require('__debug');
     // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate();
-    return false; // @oss-only
+    return true; // @oss-only
   }

   var Recoil_ReactMode = {

brilliant! thx!

ajjack50n commented 2 years ago

Guys I have put the following in my _app,tsx file but does not seem to work for me... what am I missing? I still see the error output to my console.

const memoize = (fn:any) => {
  let cache:any = {};
  return (...args:any[]):any => {
    let n = args[0];
    if (n in cache) {
      return cache[n];
    }
    else {
      let result = fn(n);
      cache[n] = result;
      return result;
    }
  }
}
// ignore in-browser next/js recoil warnings until its fixed.
const mutedConsole = memoize((console:any) => ({
  ...console,
  warn: (...args:any[]) => args[0].includes('Duplicate atom key')
    ? null
    : console.warn(...args)
}))
global.console = mutedConsole(global.console);
stavalfi commented 2 years ago

I'm using vite and I have this issue. none of the solutions are working for me so far.

ChristopherWirtOfficial commented 2 years ago

@stavalfi Vite works fundamentally differently than NextJS. You should be able to add the console.log silencer somewhere in your app structure, you can try it out by putting it inside the root index.js file of your project.

stavalfi commented 2 years ago

@ChristopherWirtOfficial can you give me a code example? vite doesn't give me access to process.stdout

arpecop commented 2 years ago

uses key: new Date().toString(), looks good so far

wachidsusilo commented 1 year ago

Not for Next.js, I'm in react with Node.js. In my case, I solved this issue by using a pacakge called uuid, which generates random numbers in Node.js. I think having the same key in recoil doesn't mean having the same recoil state value. Each time re-rendering occurs, the recoil state value changes, and a different key is required each time.

You can use it as follows

import { atom } from 'recoil';
import { v1 } from 'uuid';

function Fname(name) {
  return atom({
    key: `${name}/${v1()}`,
    default: '',
  });
}

export { Fname };

Additionally I got a name from each component using recoil value. If unnecessary, ${v1()} will be ok.

is this safe or recommended? Seems the easiest to me.

buster95 commented 1 year ago

uses key: new Date().toString(), looks good so far

how you are going to get same value?? recoil works key = value if key change every moment you never will get same value

jschaf commented 1 year ago

Recoil changed how the warning was displayed. It's now a console.error instead of console.warn and the arg is an object, not a string in this PR: https://github.com/facebookexperimental/Recoil/commit/65278038b74ab4ff82c7ded24fbb5e417f9edbd7#diff-8188127ffdb7425ff1ebdbeab7d6fe70437954a49db616d847a49b0c233274daR125

Here's updated code that works for me:

  // Hide warning about Recoil duplicate atom keys.
  // https://github.com/facebookexperimental/Recoil/issues/733#issuecomment-925072943
  const origConsole = global.console;
  const mutedConsole = (console: typeof global.console) => ({
    ...console,
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    error: (...args: any[]) => {
      if (args.length === 0 || typeof args[0] !== 'object') {
        console.error(...args);
        return;
      }
      if (typeof args[0].message === 'string' && args[0].message.includes('Duplicate atom key')) {
        return;
      }
      console.error(...args);
    },
  });
  global.console = mutedConsole(global.console);
  afterAll(() => {
    global.console = origConsole;
  });
nephix commented 1 year ago

I ditched recoil because of this

drarmstr commented 1 year ago

Feel free to suggest a PR for an optional mechanism to disable warnings.

brahn commented 1 year ago

Took a swing at this here with an environmental variable here: https://github.com/facebookexperimental/Recoil/pull/2020. Suggestions/feedback would be very welcome!

j4jefferson commented 1 year ago

@brahn Thank you. That fixed it for me.

fsyud commented 1 year ago

It happened to me, too. It's really a funny thing

yebrwe commented 1 year ago

@brahn Thank you so much!!!!

wfortin commented 1 year ago

I updated to version 0.7.6 and added the RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false environment variable and it solved it for me. I think this ticket can be closed now.