如何在没有 CSS 的情况下处理服务器组件中的响应能力?

问题描述 投票:0回答:1

这基本上是页面的根。它有 3 个组件,MainMap(客户端)和 ListCasas(服务器)。

最后的按钮更改 url searchParams 并更新 UI。现在,这段代码按预期运行(UI 方面),但在控制台中存在大量错误,因为它是客户端组件,而 ListCasas 应该是服务器。

我认为这也可能导致页面重新加载。但似乎错误从根本上归结为 useMediaQuery()。它要求客户端组件不抛出错误(useEffect,useState),所以我无法找到一种方法来使其保持服务器组件,但也知道窗口大小?

mapClientComponent.tsx 的代码

'use client';

import MainMap from '@/app/[lang]/components/main-map';
import ListCasas from '../../ui/list-casas';
import NavBar from '../components/nav';
import { Locale } from '@/i18next.config';
import { getDictionary } from '@/app/lib/dictionary';
import { getImoveis } from '@/app/lib/data';
import { Suspense, useState } from 'react';
import FiltersBar from './bar';
import { usePathname, useSearchParams } from 'next/navigation';
import { useRouter } from 'next/navigation';
import useMediaQuery from '@/hooks/mediaQuery';
// import dynamic from 'next/dynamic';
// const useMediaQuery = dynamic(() => import('@/hooks/mediaQuery'), {
//   ssr: false,
// });

export default function MapClientComponent({
  params,
  searchParams,
  mapData,
  page,
}: {
  params: { lang: Locale };
  mapData: any;
  page: any;
  searchParams?: {
    //   query?: string;
    lat?: string;
    lng?: string;
    zona?: string;
    page?: string;
    sorting?: string;
    types?: string;
    min_price?: string;
    max_price?: string;
    number_of_rooms?: string;
    exact_matches?: string;
    yt?: string;
    vr?: string;
    beach?: string;
    pool?: string;
    isMapVisible?: string;
  };
}) {
  const lg = useMediaQuery(1024);
  const isVis = searchParams?.isMapVisible || 'false';
  const isMapVisible = isVis === 'true'; // Convert to boolean

  const mapShouldShow = lg ? isMapVisible : true;
  const listShouldShow = lg ? !isMapVisible : true;

  // console.log('lg', lg);
  // console.log(isMapVisible, 'isvisible');
  // console.log(mapShouldShow, 'mapShouldShow');
  // console.log(listShouldShow, 'listShouldShow');

  //logic for visibility button
  const searchParamss = useSearchParams();
  const pathName = usePathname();
  const { replace } = useRouter();
  const paramss = new URLSearchParams(searchParamss);
  function handleVis() {
    const newIsMapVisible =
      searchParams?.isMapVisible === 'true' ? 'false' : 'true';

    paramss.set('isMapVisible', newIsMapVisible.toString());

    replace(`${pathName}?${paramss.toString()}`);
  }

  return (
    <div className="flex h-[100vh] min-w-full pt-[120px]">
      {mapShouldShow && (
        <Suspense
          key={isMapVisible.toString() + lg.toString()}
          fallback={'map loading...'}
        >
          <MainMap
            isMapVisible={isMapVisible}
            houses={mapData.houses}
            dictionary={page}
            lang={params.lang}
            defaultCenter={{ lat: searchParams?.lat, lng: searchParams?.lng }}
          />
        </Suspense>
      )}
      {listShouldShow && (
        <Suspense key={'listCasas'} fallback={'List loading...'}>
          <ListCasas
            params={{ lang: params.lang }}
            searchParams={{
              sorting: searchParams?.sorting,
              page: searchParams?.page,
              zona: searchParams?.zona,
              tipos: searchParams?.types,
              min_price: searchParams?.min_price,
              max_price: searchParams?.max_price,
              number_of_rooms: searchParams?.number_of_rooms,
              exact_matches: searchParams?.exact_matches,
              yt: searchParams?.yt,
              vr: searchParams?.vr,
              beach: searchParams?.beach,
              pool: searchParams?.pool,
            }}
          />
        </Suspense>
      )}

      <button
        className="absolute inset-x-0 bottom-6 flex justify-center text-center text-white"
        onClick={() => handleVis()}
      >
        <div className="w-[150px] bg-black p-2">
          {searchParams?.isMapVisible === 'true' ? 'Show List' : 'Show Map'}
        </div>
      </button>
    </div>
  );
}

mediaQuery.ts 代码:

import { useState, useEffect } from 'react';


const useMediaQuery = (width: number) => {
  const [targetReached, setTargetReached] = useState(false);

  const updateTarget = (e: MediaQueryListEvent) => {
    setTargetReached(e.matches);
  };

  useEffect(() => {
    const media = window.matchMedia(`(max-width: ${width}px)`);
    media.addEventListener('change', updateTarget);

    // Check on mount (callback is not called until a change occurs)
    if (media.matches) {
      setTargetReached(true);
    }

    return () => media.removeEventListener('change', updateTarget);
  }, [width]);

  return targetReached;
};

export default useMediaQuery;

UI 按预期工作,但控制台抛出错误:

CasaCard 是 ListCasas 的子项...

app-index.js:33 Warning: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding 'use client' to a module that was originally written for the server. at ListCasas

app-index.js:33 Warning: A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework. at ListCasas

Warning: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding 
'使用客户端'
 to a module that was originally written for the server. at CasaCard

app-index.js:33 Warning: A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework. at CasaCard

这些错误会成为 SSR 的问题吗?或者导致某种意想不到的错误?我不确定 UI 如何能够加载水合错误?

我真的很希望能够删除“使用客户端”。使用CSS,这将非常容易,但由于在这种情况下地图需要大量资源,我不认为CSS是一个可行的解决方案......

reactjs next.js responsive-design
1个回答
0
投票

我找到了一个替代方案,而不是在主页中调用 useMediaQuery() 并强制它成为客户端组件。

我能够将逻辑移至changeVisibility 按钮,该按钮已经是客户端。在那里,有一个 useEffect() ,它在页面加载时将重定向到当前页面,+ searchParams isMapVisible 和 isListVisible。

此方法有效并且没有错误,但想听听关于这是否是一个好方法的任何想法。

更改可见按钮:

'use client';

import useMediaQuery from '@/hooks/mediaQuery';
import { ListBulletIcon, MapIcon } from '@heroicons/react/24/outline';
import { usePathname, useSearchParams } from 'next/navigation';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

export default function ChangeVisButton(searchParams: {
  isMapVisible?: string;
  isListVisibile?: string;
}) {
  const searchParamss = useSearchParams();
  const pathName = usePathname();
  const { replace } = useRouter();
  const params = new URLSearchParams(searchParamss);

  const lg = useMediaQuery(1024);
  console.log('lg', lg);

  function handleVis() {
    const newIsMapVisible =
      searchParams.isMapVisible === 'true' ? 'false' : 'true';
    const newIsListVisible =
      searchParams.isListVisibile === 'true' ? 'false' : 'true';

    //Only does anything if screen size is SM, the button wont even show in LG
    if (lg) {
      params.set('isMapVisible', newIsMapVisible.toString());
      params.set('isListVisible', newIsListVisible.toString());
    }

    replace(`${pathName}?${params.toString()}`);
  }

  // console.log(params.get('isMapVisible'), 'ismapvisible');

  //If lg changes, alter the searchParams. Show both on lg screens, and only 1 in sm screens...
  //This will load on page load too to set the correct visibility, and pass it to params...
  useEffect(() => {
    if (!lg) {
      params.set('isMapVisible', 'true');
      params.set('isListVisible', 'true');
    } else if (lg && params.get('isMapVisible') === 'true') {
      //TODO: In here, we should be setting the isMapVisible to false. So that the list is the default
      //Its only if the user comes from LG to SM, that this triggers, NOT on-load
      //But for some reason, setting isMap to false, will cause the onLoad to malfunction
      params.set('isListVisible', 'false');
    } else {
      params.set('isMapVisible', 'false');
      params.set('isListVisible', 'true');
    }
    replace(`${pathName}?${params.toString()}`);
  }, [lg, params]);

  return (
    <button
      className="absolute inset-x-0 bottom-6 flex justify-center text-center text-white lg:hidden"
      onClick={() => handleVis()}
    >
      <div className="flex w-[145px] items-center justify-evenly rounded-lg bg-white p-2 text-black hover:bg-gray-200">
        {searchParams.isMapVisible === 'true' ? (
          <ListBulletIcon className="h-[22px] w-[22px]" />
        ) : (
          <MapIcon className="h-[22px] w-[22px]" />
        )}
        {searchParams.isMapVisible === 'true' ? 'Show List' : 'Show Map'}
      </div>
    </button>
  );
}

然后使用以下命令控制服务器组件的可见性:

{searchParams?.isMapVisible === 'true' && (

{searchParams?.isListVisible === 'true' && (
© www.soinside.com 2019 - 2024. All rights reserved.