UseMediaQuery не работает в компоненте React верхнего уровня, но работает в дочернем компоненте

Когда я использую useMediaQuery из пользовательского интерфейса материала в своем приложении React в компоненте верхнего уровня (приложении) моего приложения, запрос не работает (значение не обновляется при изменении размера окна) и вызывает проблемы с рендерингом (при использовании мобильных инструментов разработки Chrome). эмуляторы экрана). Но если я перенесу запрос в дочерний компонент (AppContainer), он сработает. Это потому, что содержащий компонент должен иметь какие-то атрибуты размера? Вот мой компонент верхнего уровня с запросом:

import firebase from './firebase/Firebase';
import {useState} from "react";
import {AuthContext} from "./contexts/AuthContext";
import useMediaQuery from "@mui/material/useMediaQuery";

function App() {

    const [isLoggedIn, setIsLoggedIn] = useState(true);
    const [user, setUser] = useState(null);
    const isWideScreen = useMediaQuery('(min-width: 500px)', {noSsr: true});

    firebase.auth().onIdTokenChanged(async (userObject) => {
        setIsLoggedIn(!!userObject);
        if (userObject){
            await localStorage.setItem('accessToken', await userObject.getIdToken());
        }
        setUser(userObject);
    })

    return (
        <AuthContext.Provider value = {{ isLoggedIn , user }}>
            <>
                <span>{'isWideScreen:'+ isWideScreen.toString()}</span>
            </>
        </AuthContext.Provider>
    );

}

export default App;

Обновление: я обнаружил, что если я закомментирую строку кода Firebase auth onIdTokenChanged, медиа-запрос будет работать. Но если я сохраню код аутентификации Firebase в этом компоненте, медиа-запрос не сможет обновиться при изменении размера.

Я не могу воспроизводить потомство. Возможно, проблема возникает тогда, когда вы пытаетесь получить доступ к ScreenContext. Можете ли вы показать код, который вызывает useContext(ScreenContext)?

Nicholas Tower 27.05.2024 18:30

Я убедился, что проблема не в поставщике контекста. Даже если я удалю контекст и использую его в качестве своего App.js, значение isWideScreen не обновляется при изменении размера окна в эмуляторе мобильного экрана Chrome dev Tools. Я обновлю исходное сообщение своим кодом.

Anand 27.05.2024 18:35

@NicholasTower Посмотрите мое обновление к посту. Я добавил код Firebase, который, похоже, вызывает проблему.

Anand 27.05.2024 19:05

Попробуйте поместить весь этот блок кода Firebase в useEffect, который запускается один раз при монтировании. Эффект будет обрабатывать добавление обработчика событий onIdTokenChanged к объекту Firebase. Функция обратного вызова по-прежнему будет запускаться каждый раз, когда пользователь меняет Firebase.

Linda Paiste 27.05.2024 19:11
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
4
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Как предложил @LindaPaiste, перемещение кода обработчика аутентификации Firebase в useEffect устранило проблему медиа-запроса.

    useEffect(() => {
    firebase.auth().onIdTokenChanged(async (userObject) => {
        setIsLoggedIn(!!userObject);
        if (userObject){
            await localStorage.setItem('accessToken', await userObject.getIdToken());
        }
        setUser(userObject);
    });
},[])
Ответ принят как подходящий

Я вижу здесь некоторые странные вещи, связанные с настройкой Firebase. Я не могу с уверенностью сказать, что это решит ваши проблемы с MUI, но я бы попробовал.

Вам нужно установить обработчик onIdTokenChanged для вашего экземпляра Firebase только один раз. Функция обратного вызова по-прежнему будет выполняться каждый раз при изменении токена идентификатора. Вы должны сделать это внутри хука useEffect с пустым массивом зависимостей [], чтобы запустить его один раз при монтировании (вы можете использовать зависимости [setIsLoggedIn, setUser], и он будет вести себя так же, поскольку эти функции никогда не меняются).

await внутри localStorage.setItem сбивает с толку. Я бы вынес это в отдельную строку. Вам не нужно awaitlocalStorage.setItem, потому что это синхронная операция.

Попробуй это:

import firebase from './firebase/Firebase';
import { useEffect, useState } from 'react';
import { AuthContext } from './contexts/AuthContext';
import useMediaQuery from '@mui/material/useMediaQuery';

function App() {

    const [isLoggedIn, setIsLoggedIn] = useState(true);
    const [user, setUser] = useState(null);
    const isWideScreen = useMediaQuery('(min-width: 500px)', { noSsr: true });

    useEffect(() => {
        firebase.auth().onIdTokenChanged(async (userObject) => {
            setIsLoggedIn(!!userObject);
            setUser(userObject);
            if (userObject) {
                const token = await userObject.getIdToken();
                localStorage.setItem('accessToken', token);
            }
        });
    }, []);

    return (
        <AuthContext.Provider value = {{ isLoggedIn, user }}>
            <>
                <span>{'isWideScreen:' + isWideScreen.toString()}</span>
            </>
        </AuthContext.Provider>
    );

}

export default App;

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