React Использовать состояние, запускаемое дважды

Я пытаюсь запустить функцию настройки один раз, когда загружается действие Discord (https://discord.com/developers/docs/activities/how-activities-work).

Мой код каким-то образом запускает функцию дважды и создает ошибку.

Вот код, который я использую:

function App() {
  const [auth, setAuth] = useState<authType>(null);

  async function setupDiscordSdk() {
    const CLIENT_ID = "my client id";
    const discordSdk = new DiscordSDK(CLIENT_ID, { disableConsoleLogOverride: true });
    await discordSdk.ready();
    console.warn('DISCORD SDK READY!') // This somehow calls twice

    // Authorize with Discord Client
    const { code } = await discordSdk.commands.authorize({
      client_id: CLIENT_ID,
      response_type: "code",
      state: "",
      prompt: "none",
      scope: [
        "identify",
      ],
    });

    // Retrieve an access_token from your activity's server
    const response = await fetch("/api/token", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        code,
      }),
    });

    const { access_token, publicProfile, authkey } = await response.json();
    localStorage.setItem('bread:authkey', authkey);

    // Authenticate with Discord client (using the access_token)
    let auth = (await discordSdk.commands.authenticate({
      access_token,
    }) as authTypeNOTNULL);

    auth.publicProfile = publicProfile as boolean;
    auth.user.avatarURL = (auth.user.avatar ? `https://cdn.discordapp.com/avatars/${auth.user.id}/${auth.user.avatar}.png?size=256` : `https://cdn.discordapp.com/embed/avatars/${(BigInt(auth.user.id) >> 22n) % 6n}.png`);
    auth.discordSdk = discordSdk;
    setAuth(auth);
    console.warn('AUTHED SUCCESSFULLY.');
  };

  async function openLink(url: string) {
    console.warn('Trying to open URL', url);
    console.info(auth)
    if (!auth) return console.error('Not authed');
    return await auth.discordSdk.commands.openExternalLink({ url });
  };

  useEffect(() => console.info(auth), [auth]);

  useEffect(() => {
    setupDiscordSdk().catch(console.error);
  }, [])

  return (
    <>
      ...
    </>
  )
}

export default App;

Все остальное работает, только авторизации нет. Кто-нибудь может мне помочь, пожалуйста!

Заранее спасибо.

в режиме разработки реакция выполняет двойной рендеринг, чтобы обнаружить подобные ошибки. начал бы с добавления флага, который проверяет, что аутентификация идентификатора уже выполняется, и учитывает, что вам может не понадобиться метод setupDiscordSdk внутри эффекта использования.

danielRICADO 24.06.2024 00:26

Вне эффекта использования он будет выполняться при каждом повторном рендеринге, верно? А я только что попробовал, тоже два раза рендерится и выдает ошибку

Kodeur_Kubik 24.06.2024 01:14

год, если на уровне компонента приложения будет выполняться повторная обработка, это не будет для вас вариантом, в этом случае вы, вероятно, захотите попробовать «флаг выполнения аутентификации». Как и ответ, приведенный ниже, но вам нужно вручную установить флаг перед началом запроса, а не просто проверять завершение аутентификации, как предложил другой пользователь.

danielRICADO 24.06.2024 01:20

Создайте свое приложение локально. Это почти наверняка больше не повторится, но React делает это намеренно. Они пытаются заставить вас ловить подобные ошибки сейчас, а не полагаться на время, которое обычно работает. После удаления строгого режима в режиме разработки он будет запускаться только один раз, но для этого не следует удалять строгий режим. Попробуйте создать ссылку на загрузку и установить для нее значение true при отправке первоначального запроса, а затем значение false, когда обещание будет разрешено, и проверьте эту ссылку перед отправкой запроса.

Andrew 24.06.2024 02:30

Я пытаюсь это сделать с помощью этого кода: асинхронная функция setupDiscordSdk() { console.warn('Пытаюсь войти в систему', isAuthing); если (isAuthing) возврат; setIsAuthing (истина); ... но он дважды регистрирует «попытку входа в систему - false».. (isAuthing - это useState(false))

Kodeur_Kubik 24.06.2024 12:49

Обновлено: добавил useRef (не знал, что он делает, лол), и, похоже, он работает

Kodeur_Kubik 24.06.2024 13:02
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
6
72
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Функция setupDiscordSdk может запускаться дважды из-за находящегося в разработке React StrictMode, который намеренно дважды вызывает определенные методы жизненного цикла (включая useEffect).

можете ли вы просто добавить условие if в свой useEffect?

 useEffect(() => {
    if (!auth) setupDiscordSdk()
  }, [auth]);

Это тоже не работает, та же проблема: происходит двойной рендеринг и ошибка.

Kodeur_Kubik 24.06.2024 01:12

проблема в том, что запрос отправляется дважды, прежде чем завершиться. добавьте в свой ответ собственный флаг, который переключается до начала запроса и после его завершения, и вы будете близки к правильному ответу;)

danielRICADO 24.06.2024 01:23
Ответ принят как подходящий

Исправил с помощью комментариев и самостоятельно. Вот мой обновленный код:

function App() {
  const [auth, setAuth] = useState<authType>(null);
  const [isAuthing, setIsAuthing] = useState(false);
  const [showLeaderboard, setShowLeaderboard] = useState(false);
  const hasInitialized = useRef(false);

  async function setupDiscordSdk() {
    console.warn('Trying to login');
    if (isAuthing) return;
    setIsAuthing(true);

    const CLIENT_ID = "1170963763340517466";
    const discordSdk = new DiscordSDK(CLIENT_ID, { disableConsoleLogOverride: true });
    await discordSdk.ready();
    console.warn('DISCORD SDK READY!')

    // ...

    setAuth(authing);
    console.warn('AUTHED SUCCESSFULLY.');
    setIsAuthing(false);
  }

  useEffect(() => {
    if (!hasInitialized.current) {
      setupDiscordSdk();
      hasInitialized.current = true;
    }
  }, []);

К вашему сведению, использование ссылок «isMounted», «isInitialized» и т. д. уже некоторое время считается антишаблоном. Вы оптимизируете непроизводственную «проблему».

Drew Reese 26.06.2024 09:10

Другие вопросы по теме