Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
janus-reith opened this issue Nov 11, 2020 · 64 comments
Labels
help wanted Extra attention is needed

Comments

@janus-reith
Copy link

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
Copy link

Also mentioned in:
#213 (comment)

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

@leonardomgt
Copy link

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

@janus-reith
Copy link
Author

@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 drarmstr changed the title Duplicate atom key during development and during production build in nextjs [SSR][NextJS] Duplicate atom key during development and during production build in nextjs Nov 17, 2020
@drarmstr
Copy link
Contributor

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

@alexilyaev
Copy link

@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
Copy link
Contributor

Related to #247

@janus-reith
Copy link
Author

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
Copy link

Him-2C commented Dec 9, 2020

Me to for this issue.

@juanpprieto
Copy link

juanpprieto commented Feb 26, 2021

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

@juanpprieto
Copy link

juanpprieto commented Apr 5, 2021

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
Copy link

adamhenson commented Jul 17, 2021

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
Copy link

juanpprieto commented Jul 19, 2021

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
Copy link

R-Bower commented Sep 16, 2021

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
Copy link

juanpprieto commented Sep 16, 2021

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
Copy link

R-Bower commented Sep 21, 2021

@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
Copy link

juanpprieto commented Sep 22, 2021

@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
Copy link

R-Bower commented Sep 22, 2021

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
Copy link

juanpprieto commented Sep 22, 2021

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
Copy link

juanpprieto commented Sep 22, 2021

@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
Copy link

hsh2001 commented Sep 28, 2021

@juanpprieto It works! Thanks :)

@spookyvert
Copy link

@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

@juanpprieto
Copy link

we got there in the end <3

@christian-gama
Copy link

Just faced this issue... Came here and saw this patch 0.7.6 released yesterday. Am I lucky? 😆

@aam051102
Copy link

@wfortin is correct. Recoil 0.7.6 with RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false in .env solved the issue in our NextJS project. Thank you very much for that information.

@coderhzy
Copy link

const intercept = require('intercept-stdout');

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

intercept(interceptStdout);

@Mecil9
Copy link

Mecil9 commented Nov 18, 2022

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.

I've been waiting a year for this answer!!!!!!

@hamtarodev
Copy link

how can i do this with Vite? on Vite it requires that the env variables should start with VITE_

@satokoki645
Copy link

how can i do this with Vite? on Vite it requires that the env variables should start with VITE_

I was thinking this same thing.

@ThomasCarstens
Copy link

@hamtarodev @satokoki645 The recoil changelog will help you, as of 6/10/22: https://yarnpkg.com/package/recoil

@tesseractjh
Copy link

tesseractjh commented Jan 22, 2023

@ThomasCarstens Thanks!
@hamtarodev @satokoki645 RecoilEnv can solve it with Vite.

// configs/recoil.ts
import { RecoilEnv } from 'recoil';

RecoilEnv.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED = false;
// App.tsx
import './configs/recoil';

function App() {
  return (
    <RecoilRoot>
        { ... }
    </RecoilRoot>
  );
}

@qqww08
Copy link

qqww08 commented Jan 24, 2023

Thanks to this, I was able to remove the console log from nextJS. This is my code

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  env: {
// @see https://github.com/facebookexperimental/Recoil/issues/2135#issuecomment-1362197710
    RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED: "false",
  }
};

module.exports = nextConfig;

@LOGANLEEE
Copy link

@ThomasCarstens Thanks! @hamtarodev @satokoki645 RecoilEnv can solve it with Vite.

// configs/recoil.ts
import { RecoilEnv } from 'recoil';

RecoilEnv.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED = false;
// App.tsx
import './configs/recoil';

function App() {
 return (
   <RecoilRoot>
       { ... }
   </RecoilRoot>
 );
}

solved problem.

in my case

  • console error on browser but not IDE if I set on env
  • no console error on browser but on IDE if I set on next.config.js

lovely thanks.

KIMSEUNGGYU added a commit to YAPP-Github/21st-Web-Team-2-FE that referenced this issue Jan 26, 2023
KIMSEUNGGYU added a commit to YAPP-Github/21st-Web-Team-2-FE that referenced this issue Jan 29, 2023
* [feat] Login UI 구현

* [feat] ContentLayout UI 구현
- 로그인 화면, 온보딩과 같은 싱글 컨텐츠 레이아웃 UI 구현

* [feat] LoginPage UI 구현

* [feat] signin api 기능 및 msw 구현

* [feat] google oauth 로그인 후 헤더 UI 구현

* [feat] recoil 관련 storybook 설정

* [feat] 커먼성 UserInfo UI 구현
- User 의 정보를 나타내는 UserInfo 컴포넌트 구현

* [feat] 로그인 성공후 유저 정보를 클릭 시 나타나는 UserMenu UI 구현

* [feat] User UI 클릭 시 유저메뉴 보이는 기능 구현

* [feat] recoil localstorage effect 기능 구현
- 로그인 성공시 해당 user 정보를 localstorage 에 저장하여 영구적으로 사용할 수 있도록 설정

* [feat] google oauth redirect 기능 구현

* [fix] 🐛 nextjs Hydration failed error 수정
- recoil effect 로 localstorage 에 user 값이 있냐 없냐에 따라 로그인 상태 여부를 판단하는데 해당 기능을 추가할 때 Hydration failed 에러 발생
=> 유추한 결과 window undefined 를 판단하는데 이때 초기 렌더링 UI 와 달라 발생하는 거 같음
- 해당 URL 참고하여 수정 https://stackoverflow.com/questions/71706064/react-18-hydration-failed-because-the-initial-ui-does-not-match-what-was-render/71797054#71797054

* [feat] api 없는 로그아웃 기능 구현

* [fix] 🐛 recoil 0.7.6  Duplicate atom key error 수정
- atomKey 가 고유한데 해당 이슈 발생해서 찾아보니 recoil 0.7.6 이슈 같음
https://stackoverflow.com/questions/65506656/recoil-duplicate-atom-key-in-nextjs
facebookexperimental/Recoil#733 (comment)

* [refactor] type 수정

* [feat] env-sample 에 NEXT_PUBLIC_GOOGLE_CLIENT_ID 형식 추가

* [chore] chromatic 배포를 위한 환경설정 값 추가

* [chore] 실수로 올린 .env 파일 제거

* [chore] 실수로 변경한 config 설정 수정

* [refactor] 코드리뷰 적용
- auth Type 수정
- resetUser 네이밍 수정
- UserInfo 컴포넌트 default type 수정
- 불필요한 코드 제거

* [refactor] login page 구조 변경
@MejanH
Copy link

MejanH commented Feb 17, 2023

Just because of this issue I had to move to Jotai. disabling the Duplicate key checker is not a fix. now, recoil won't check for duplicate keys.

@SGDS666
Copy link

SGDS666 commented May 4, 2023

I tried all the above methods but they didn't solve my problem. Every time I refresh the page, I report an error, so I rudely used the following method to solve the problem

console.error = (messages, ...rest) => {
  if (messages?.message?.includes("Duplicate atom key")) {
    return
  }
  console.warn(messages, ...rest)
}

pocojang pushed a commit to woowacourse/react-shopping-cart that referenced this issue May 19, 2023
* chore: vite 초기 설정

* docs: 기능 요구 사항 반영하여 구현 목록 작성

Co-authored-by: Gabriel Ju Hyun, Yoon <gabrielyoon7@gmail.com>

* chore: github page 자동 배포 설정

* chore: storybook 설치 및 자동배포 설정

* test: product item 컴포넌트 스토리북 테스트 추가

* feat: product item 컴포넌트 뼈대 구현

* chore: styled-components 및 해당 타입 설치

* feat: product item 컴포넌트 스타일 구현

* test: product item 컴포넌트 스타일북 케이스 추가

* feat: product item type 별도로 분리

* feat: CartController 컴포넌트 구현 및 적용

* test: CartController 스토리 추가

* refactor: ProductItem 스타일 높이 변경 및 배경 색상 제거

* refactor: 사용하지 않는 App, index 스타일 제거

* chore: svg 이미지 파일 추가 및 타입 설정

* feat: global style 적용하여 기본 스타일링 reset

* chore: svg 파일 타입 컴포넌트로 지정

* feat: Header 컴포넌트 스타일 적용하여 구현

* test: Header 컴포넌트 스토리 테스트 추가

* refactor: 프로젝트 title 변경

* feat: style theme 적용

* test: header 컴포넌트 스토리에 providers 적용

* fix: globalStyle 오탈자 수정

* test: ProductItem 컴포넌트 스토리북에 providers 적용

* chore: 배포 중 eslint 적용하지 않도록 설정

* fix: 배포시 빌드 버그 수정

* fix: 사용하지 않는 라이브러리 import 제거

* chore: react-router-dom 설치

* feat: react router dom을 위한 공통 레이아웃 구현 및 적용

* fix: base url 버그 수정

* fix: storybook 테스트를 위한 router-dom providers에 추가

* chore: recoil 설치

* feat: 상품 리스트 mock 데이터 생성

* feat: recoil root 적용 및 providers에 추가

* feat: Home 컴포넌트 구현 및 상품 리스트를 mock api로 구현

* feat: cart list 상태로 관리 및 장바구니 등록 버튼 클릭시 상태 변경 구현

* feat: 장바구니 리스트 수량 조절 기능 구현

* fix: useCart 커스텀 훅 mockAPI 통신 순서 변경

* fix: CartController 컴포넌트 자체로 갖고 있던 quantity 상태 제거

* refactor: useCart 커스텀훅 반복되는 로직 추상화

* feat: 반응형 ui 적용

* refactor: CartController 컴포넌트 높이 등 세부 스타일링 수정

* feat: mockApi 적용하여 장바구니 아이템 local storage에 등록 구현

* feat: mockApi 이용하여 장바구니 업데이트 및 local storage 등록

* feat: 상품 리스트 local storage 등록 및 로딩 시 불러오기 구현

* feat: mockApi 이용하여 장바구니 아이템 삭제 기능 구현

* fix: 컴포넌트 수정으로 인한 storybook 수정사항 적용

* refactor: 사용하지 않는 지역변수 제거

* docs: 기능 요구사항 문서 최신화

* refactor: 기본 이미지를 고양이에서 다른 이미지로 변경

* refactor: atom 모듈화

* fix: 수량이 100개 이상 설정되는 문제 수정

* docs: 기능 목록 요구사항 최신화

* docs: readme 작성

* style: 불필요한 코드 제거 및 코드 간격 조정 등

* feat: 데이터 처리 중 오류 발생 시 콘솔에 결과가 출력되는 기능 구현

* Update readme.md

* Update readme.md

* refactor: html lang을 en 에서 ko 로 변경

* refactor: 과도한 래핑 함수 제거

* refactor: 가독성 개선을 위해 quantity 삼항연산자 분리

* style: jsx self-closing element 적용

* style: 불필요한 주석 제거

* refactor: 검색한 카트의 수량을 nullish coalescing operator으로 접근하도록 개선

* refactor: 타입 import 방식 변경

- Type-Only Imports and Exports 로 변경
- https://www.typescriptlang.org/ko/docs/handbook/release-notes/typescript-3-8.html

* refactor: import * as S from ... 표기법 제거

* refactor: 신규 장바구니 아이템 추가 전 검사하는 로직 개선

* style: 불필요한 Fragment 제거

* refactor: addCart에서 이미 있는 장바구니 아이템이 발견되는 경우 반환되는 로직을 개선

* style: 주석 스타일 개선

* refactor: 장바구니 페이지 품목 임시 표시용 텍스트 스타일 변경

* chore: storyvook v6 => v5.3.10으로 다운그레이드

* chore: 미사용 패키지 제거

* refactor: newCartItem을 타입으로 강제하도록 개선

* refactor: nested object 의 프로퍼티 값을 수정하기 위한 새로운 함수 구현
- map 함수를 제거

* chore: recoil-persist 설치

* refactor: mockApi 제거

- recoil-persist를 활용하여 장바구니 리스트를 세션 스토리지에서 관리

- 추후 msw로 이전하기 위한 기초 작업

* refactor: ProductList 컴포넌트 추가

- useRecoilValueLoadable에서 데이터를 받아오도록 개선

* refactor: 불필요한 커스텀훅 제거

* refactor: 장바구니 갯수를 selector로 계산하도록 개선

* refactor: 함수명 변경

* refactor: 장바구니 수량 조회 로직 개선

- atoms selector로 조회

* fix: vite 환경에서의 Duplicate atom key 에러 해결

facebookexperimental/Recoil#733 (comment)

* fix: selector 버그 수정을 위해 원상 복구

* refactor: recoil-persist 제거

- recoil 상태 유지를 하지 않는 것으로 계획 변경

* chore: msw 설치 및 기본 세팅

* refactor: 상품 목록을 fetch에 연결 및 msw 모킹 구현

* chore: react-error-boundary 설치

* refactor: 상품 리스트 수신 에러 핸들링

* refactor: 장바구니 목록을 msw로 mocking

* refactor: 신규 장바구니 추가 fetch 추가 및 msw mocking 구현

* refactor: 삭제 fetch 구현 (msw)

* refactor: 장바구니 아이템 수량 변경 fetch 코드 구현 및 msw mocking

* refactor: mockData 이미지 주소를 http => https 변경

* fix: 배포시 이미지가 뜨지 않는 문제 수정

* fix: 이미지 요청이 필터링 되는 문제 수정

* refactor: api 계층 분리

* fix: 이미지 버그 수정

- 기존에 사용한 임시 파일이  https가 아닌 http로 변환되는 문제가 있었음.

* style: 불필요한 코드 제거

* refactor: 불필요한 코드 제거

---------

Co-authored-by: ukkodeveloper <ukkodeveloper@gmail.com>
pocojang pushed a commit to woowacourse/react-shopping-cart that referenced this issue May 28, 2023
* chore: vite 초기 설정

* docs: 기능 요구 사항 반영하여 구현 목록 작성

Co-authored-by: Gabriel Ju Hyun, Yoon <gabrielyoon7@gmail.com>

* chore: github page 자동 배포 설정

* chore: storybook 설치 및 자동배포 설정

* test: product item 컴포넌트 스토리북 테스트 추가

* feat: product item 컴포넌트 뼈대 구현

* chore: styled-components 및 해당 타입 설치

* feat: product item 컴포넌트 스타일 구현

* test: product item 컴포넌트 스타일북 케이스 추가

* feat: product item type 별도로 분리

* feat: CartController 컴포넌트 구현 및 적용

* test: CartController 스토리 추가

* refactor: ProductItem 스타일 높이 변경 및 배경 색상 제거

* refactor: 사용하지 않는 App, index 스타일 제거

* chore: svg 이미지 파일 추가 및 타입 설정

* feat: global style 적용하여 기본 스타일링 reset

* chore: svg 파일 타입 컴포넌트로 지정

* feat: Header 컴포넌트 스타일 적용하여 구현

* test: Header 컴포넌트 스토리 테스트 추가

* refactor: 프로젝트 title 변경

* feat: style theme 적용

* test: header 컴포넌트 스토리에 providers 적용

* fix: globalStyle 오탈자 수정

* test: ProductItem 컴포넌트 스토리북에 providers 적용

* chore: 배포 중 eslint 적용하지 않도록 설정

* fix: 배포시 빌드 버그 수정

* fix: 사용하지 않는 라이브러리 import 제거

* chore: react-router-dom 설치

* feat: react router dom을 위한 공통 레이아웃 구현 및 적용

* fix: base url 버그 수정

* fix: storybook 테스트를 위한 router-dom providers에 추가

* chore: recoil 설치

* feat: 상품 리스트 mock 데이터 생성

* feat: recoil root 적용 및 providers에 추가

* feat: Home 컴포넌트 구현 및 상품 리스트를 mock api로 구현

* feat: cart list 상태로 관리 및 장바구니 등록 버튼 클릭시 상태 변경 구현

* feat: 장바구니 리스트 수량 조절 기능 구현

* fix: useCart 커스텀 훅 mockAPI 통신 순서 변경

* fix: CartController 컴포넌트 자체로 갖고 있던 quantity 상태 제거

* refactor: useCart 커스텀훅 반복되는 로직 추상화

* feat: 반응형 ui 적용

* refactor: CartController 컴포넌트 높이 등 세부 스타일링 수정

* feat: mockApi 적용하여 장바구니 아이템 local storage에 등록 구현

* feat: mockApi 이용하여 장바구니 업데이트 및 local storage 등록

* feat: 상품 리스트 local storage 등록 및 로딩 시 불러오기 구현

* feat: mockApi 이용하여 장바구니 아이템 삭제 기능 구현

* fix: 컴포넌트 수정으로 인한 storybook 수정사항 적용

* refactor: 사용하지 않는 지역변수 제거

* docs: 기능 요구사항 문서 최신화

* refactor: 기본 이미지를 고양이에서 다른 이미지로 변경

* refactor: atom 모듈화

* fix: 수량이 100개 이상 설정되는 문제 수정

* docs: 기능 목록 요구사항 최신화

* docs: readme 작성

* style: 불필요한 코드 제거 및 코드 간격 조정 등

* feat: 데이터 처리 중 오류 발생 시 콘솔에 결과가 출력되는 기능 구현

* Update readme.md

* Update readme.md

* refactor: html lang을 en 에서 ko 로 변경

* refactor: 과도한 래핑 함수 제거

* refactor: 가독성 개선을 위해 quantity 삼항연산자 분리

* style: jsx self-closing element 적용

* style: 불필요한 주석 제거

* refactor: 검색한 카트의 수량을 nullish coalescing operator으로 접근하도록 개선

* refactor: 타입 import 방식 변경

- Type-Only Imports and Exports 로 변경
- https://www.typescriptlang.org/ko/docs/handbook/release-notes/typescript-3-8.html

* refactor: import * as S from ... 표기법 제거

* refactor: 신규 장바구니 아이템 추가 전 검사하는 로직 개선

* style: 불필요한 Fragment 제거

* refactor: addCart에서 이미 있는 장바구니 아이템이 발견되는 경우 반환되는 로직을 개선

* style: 주석 스타일 개선

* refactor: 장바구니 페이지 품목 임시 표시용 텍스트 스타일 변경

* chore: storyvook v6 => v5.3.10으로 다운그레이드

* chore: 미사용 패키지 제거

* refactor: newCartItem을 타입으로 강제하도록 개선

* refactor: nested object 의 프로퍼티 값을 수정하기 위한 새로운 함수 구현
- map 함수를 제거

* chore: recoil-persist 설치

* refactor: mockApi 제거

- recoil-persist를 활용하여 장바구니 리스트를 세션 스토리지에서 관리

- 추후 msw로 이전하기 위한 기초 작업

* refactor: ProductList 컴포넌트 추가

- useRecoilValueLoadable에서 데이터를 받아오도록 개선

* refactor: 불필요한 커스텀훅 제거

* refactor: 장바구니 갯수를 selector로 계산하도록 개선

* refactor: 함수명 변경

* refactor: 장바구니 수량 조회 로직 개선

- atoms selector로 조회

* fix: vite 환경에서의 Duplicate atom key 에러 해결

facebookexperimental/Recoil#733 (comment)

* fix: selector 버그 수정을 위해 원상 복구

* refactor: recoil-persist 제거

- recoil 상태 유지를 하지 않는 것으로 계획 변경

* chore: msw 설치 및 기본 세팅

* refactor: 상품 목록을 fetch에 연결 및 msw 모킹 구현

* chore: react-error-boundary 설치

* refactor: 상품 리스트 수신 에러 핸들링

* refactor: 장바구니 목록을 msw로 mocking

* refactor: 신규 장바구니 추가 fetch 추가 및 msw mocking 구현

* refactor: 삭제 fetch 구현 (msw)

* refactor: 장바구니 아이템 수량 변경 fetch 코드 구현 및 msw mocking

* refactor: mockData 이미지 주소를 http => https 변경

* fix: 배포시 이미지가 뜨지 않는 문제 수정

* fix: 이미지 요청이 필터링 되는 문제 수정

* refactor: api 계층 분리

* fix: 이미지 버그 수정

- 기존에 사용한 임시 파일이  https가 아닌 http로 변환되는 문제가 있었음.

* style: 불필요한 코드 제거

* refactor: 불필요한 코드 제거

* refactor: 버튼 디자인 개선

* refactor: 헤더 디자인 개선

* feat: 장바구니 기본 html 뼈대 작업

* feat: 장바구니 컴포넌트 뼈대 추가

* refactor: 장바구니 아이템 기본 디자인 추가

* refactor: 장바구니 목록 디자인 뼈대 구현

* chore: eslint 규칙 추가

* refactor: 테마의 색상을 nested 객체로 수정

* refactor: 변수명 변경
- Column => Col
- Cart => CartItem

* fix: 오탈자 수정

* refactor: CartList 컴포넌트 분리

* feat: 구매 박스 모듈화 및 기본 디자인 적용

* refactor: 장바구니 페이지 디자인 개선 작업

- 구매 박스 모듈화
- 장바구니 페이지 내 간격 및 구조 개선 등

* fix: 브랜치 변경으로 인해 자동 배포가 되지 않던 문제 해결

* refactor: fetchAddCart 로직 개선

* refactor: useCart 코드 정리

- 일부 코드 모듈화
- 과도한 연산을 요구하는 함수의 역할을 이전

등

* feat: 장바구니 아이템이 선택됐는지 확인할 수 있는 체크용 프로퍼티 추가

- 기본 값으로 참(체크됨) 처리

* feat: 장바구니 페이지에서 아이템을 직접 삭제할 수 있는 기능 추가

* feat: 모든 아이템이 선택되었을 때 전체 선택이 자동으로 체크 처리 되는 기능 구현

* feat: 장바구니 아이템 구매여부 체크기능 구현

* fix: storybook 배포 버그 문제 수정

- 프로퍼티 변경 반영

* feat: 장바구니 리스트 전체 선택/해제 기능 구현

* feat: 장바구니 리스트에서 현재 몇 개의 상품이 선택됐는지 확인하는 기능 구현

* feat: 장바구니 결제 금액 계산기 구현

* refactor: 장바구니 리스트와 구매 박스 styled-components 추가 적용

* feat: 장바구니 리스트가 비어있을 때 대응하는 페이지 추가

* test: 스토리북에서 장바구니 아이템을 아주 많이 추가했을 때의 케이스를 추가

* feat: 장바구니 페이지에 반응형 시스템 추가

* refactor: 숫자 표기가 1,000단위에서 끊기게 수정

* refactor: 디자인 개선

- 결제 텍스트 간격 개선
- 휴지통에 커서 효과 적용

* feat: 물품 삭제 시 확인 문구 출력하는 기능 추가

* refactor: recoil 상태관리에 필요한 selectors 구조 개선

* feat: 상품에 hover 효과 적용

* feat: 상품에 스켈레톤 효과 적용

* feat: 상품 정보를 출력할 모달 추가

* feat: 상품 정보를 출력할 모달 구현 및 디자인 완료

* feat: 상품에 장바구니 수량이 뜨는 디자인 기능 구현

* feat: 장바구니에서 빈 공간을 눌렀을 때의 상호작용 기능 추가

- 장바구니 쉽게 체크하기
- 장바구니 전체 쉽게 체크하기

* feat: 선택 삭제 기능 구현

* refactor: 상품 조회 모달 디자인 개선

* refactor: 장바구니 수량 조절 디자인 개선

* refactor: Modal 컴포넌트의 styled 모듈화

* refactor: 상품 모달 코드 구조 개선

- styled 모듈화

* refactor: selectFamily를 활용하여 특정 상품의 장바구니 수량을 얻을 수 있도록 로직 개선

* refactor: selectFamily를 활용하여 특정 상품의 장바구니 수량을 수정할 수 있는 로직 구현

* refactor: updateCartListQuantitySelector에서도 fetch가 되도록 개선 및 기존 코드 제거

* refactor: 불필요한 함수 제거

* refactor: 함수명 변경

- updateCartListQuantitySelector => updateCartItemQuantitySelector

* refactor: 장바구니 아이템을 추가하는 로직 개선

* refactor: 장바구니 아이템을 삭제하는 로직 개선

* refactor: 장바구니 아이템 수량 조절기로 삭제하는 로직 복구

* refactor: 선택된 장바구니 아이템을 삭제하는 기능 로직 개선

* refactor: 장바구니 구매 체크박스 선택 로직 개선

* refactor: 장바구니 전체 선택 기능 로직 개선

- 커스텀훅 완전 제거

* fix: 배포 버그 수정

* chore: 스토리북 배포 설정 수정

* fix: 스토리북 중복 키 문제 수정

* Update readme.md

* style: 전체 프로젝트 prettier 적용

* fix: 병합 충돌 문제 해결

* refactor: 불필요한 코드 제거

* refactor: 카트 아이콘 코드 개선

* refactor: 상품 목록을 요청하는 기능을 모듈화 하기 위해 컴포넌트로 분리

* refactor: 불필요한 변수 제거

* refactor: 모든 장바구니 아이템 체크 여부 확인 로직 개선

* refactor: useRecoilCallback를 활용한 상태 업데이트 로직 개선

- useSetRecoilState에서 인자를 반드시 넘겨줘야 하는 문제를 해결하기 위해 useRecoilCallback을 사용

- https://recoiljs.org/docs/api-reference/core/useRecoilCallback/

---------

Co-authored-by: ukkodeveloper <ukkodeveloper@gmail.com>
@mickdewald
Copy link

Just because of this issue I had to move to Jotai. disabling the Duplicate key checker is not a fix. now, recoil won't check for duplicate keys.

It's said, I know.
I will check out Zustand, as I already have for my mobile app. It works like a charm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.