当用户选择产品时,如何使用效果和参考来设置自动滚动行为?

How do I use effects and refs to set an automatic scroll behavoir when a user selects a product?

提问人:Garrett Rozsa 提问时间:10/20/2023 更新时间:10/20/2023 访问量:15

问:

我正在使用 tailwind CSS 在 Next 中工作。目标是设置一个商店页面,其行为类似于 instagram 提要,用户在其中单击网格中的产品照片,该页面在占据整个视口的 FeaturedProduct 卡片上呈现所有相同的内容,并且可以滚动以查看整个目录,其显示顺序与在 renderProductList 中定义的网格视图中的显示顺序相同。

这个组件几乎是完美的。为了完成它,我需要在布局更改以响应用户单击产品时滚动到视图中。

import React, { useContext, useState, useEffect, useRef } from 'react';
import FeaturedProduct from '@/components/featured-product';
import { ProductsContext } from '@/context/products-context';

const Home = () => {
  const [selectedProduct, setSelectedProduct] = useState(null);
  const products = useContext(ProductsContext);

  const containerRef = useRef(null);
  const productRefs = useRef([]);

  const handleProductClick = (product) => {
    setSelectedProduct(product);
  };

  useEffect(() => {
    console.log('useEffect called');

    if (selectedProduct && productRefs.current[selectedProduct.id]) {
      const productElement = productRefs.current[selectedProduct.id];
      const containerElement = containerRef.current;
      if (productElement && containerElement) {
        const containerRect = containerElement.getBoundingClientRect();
        const productRect = productElement.getBoundingClientRect();
        const offset = productRect.top - containerRect.top - (containerRect.height - productRect.height) / 2;

        containerElement.scrollBy({
          top: offset,
          behavior: 'auto',
        });
      }
    }
  }, [selectedProduct]);

  const renderProductList = () => {
    return (
      <div className='grid grid-cols-3 gap-4' ref={containerRef}>
        {products.map((product, index) => (
          <div 
          key={product.id} 
          ref={(el) => (productRefs.current[product.id] = el)}
          onClick={() => handleProductClick(product)}
          >
            <img
            src={product.imageUrl}
            alt={product.title} 
            className='w-full h-auto cursor-pointer'
            />
          </div>
        ))}
      </div>
    );
  };

  const renderFullScreenProduct = () => {
    if (!selectedProduct) return null;

    const selectedProductIndex = products.findIndex((product) => product.id === selectedProduct.id);

    const allProducts = products.map((product, index) => (
    <div key={product.id} ref={(el) => index === selectedProductIndex && el}>
      <FeaturedProduct { ...product } key={product.id} />
    </div>
  ));

    return (
      <div className='fixed top-0 left-0 w-full h-full overflow-y-auto bg-white'>
        <div className='max-w-screen-md mx-auto p-4' >
          {allProducts}
          <button
            className='absolute top-4 right-4 text-gray-600'
            onClick={() => setSelectedProduct(null)}
          >
            Close
          </button>
        </div>
      </div>
    );
  };

  return (
    <div className='mx-4'>
      {selectedProduct ? renderFullScreenProduct() : renderProductList()}
    </div>
  );
};

export default Home;


这是我得到的最好的。我没有足够的经验使用 useRef 和 useEffect 来使这项工作对我有利。我想重申,这个组件的行为几乎完全符合我的需要。设置自动滚动行为似乎是一件非常容易的事情,我相信解决方案比我想出的这个绝对混乱要优雅得多。

next.js 反应钩子 副作用

评论

0赞 nullptr 10/20/2023
文档中的这个例子基本上正是你想要的
0赞 Garrett Rozsa 10/21/2023
谢谢nullptr,这正是我想做的。

答: 暂无答案