Saya cuba membina versi mudah alih halaman utama saya dan nampaknya terdapat pepijat dengan "item" akordion bersarang saya di mana ia tidak memaparkan ketinggian yang betul bagi bahagian item bawah apabila mula-mula dibuka.
Untuk membukanya, anda mula-mula klik pada teks projek, kemudian ia menyenaraikan projek, kemudian klik pada projek untuk menogol kad projek.
(Dikemas kini) Saya percaya ini berlaku kerana akordion ibu bapa saya tidak mengemas kini ketinggiannya apabila akordion kanak-kanak dibuka.
Adakah anda tahu cara yang baik untuk melakukan ini? Atau jika perlu, patutkah saya menyusun semula komponen saya dengan cara yang membolehkan perkara ini berlaku? Kesukarannya ialah Accordion menerima kanak-kanak, dan saya menggunakan semula Accordion di dalamnya, jadi ia agak mengelirukan. Saya tahu saya boleh menggunakan fungsi panggil balik untuk mencetuskan ibu bapa, tetapi tidak pasti bagaimana untuk melakukannya.
Rumah.tsx
import { Accordion } from "@/components/atoms/Accordion" import { AccordionGroup } from "@/components/atoms/AccordionGroup" import { AccordionSlideOut } from "@/components/atoms/AccordionSlideOut" import { Blog } from "@/components/compositions/Blog" import { Contact } from "@/components/compositions/Contact" import { Portfolio } from "@/components/compositions/Portfolio" import { PuyanWei } from "@/components/compositions/PuyanWei" import { Resumé } from "@/components/compositions/Resumé" import { Socials } from "@/components/compositions/Socials" import { Component } from "@/shared/types" interface HomepageProps extends Component {} export function Homepage({ className = "", testId = "homepage" }: HomepageProps) { return ( <main className={`grid grid-cols-12 pt-24 ${className}`} data-testid={testId}> <section className="col-span-10 col-start-2"> <AccordionGroup> <Accordion title="Puyan Wei"> <PuyanWei /> </Accordion> <Accordion className="lg:hidden" title="Portfolio"> <Portfolio /> </Accordion> <AccordionSlideOut className="hidden lg:flex" title="Portfolio"> <Portfolio /> </AccordionSlideOut> <Accordion title="Resumé"> <Resumé /> </Accordion> <Accordion title="Contact"> <Contact /> </Accordion> <Accordion title="Blog"> <Blog /> </Accordion> <Accordion title="Socials"> <Socials /> </Accordion> </AccordionGroup> </section> </main> ) }
portfolio.tsx
import { Accordion } from "@/components/atoms/Accordion" import { AccordionGroup } from "@/components/atoms/AccordionGroup" import { ProjectCard } from "@/components/molecules/ProjectCard" import { projects } from "@/shared/consts" import { Component } from "@/shared/types" interface PortfolioProps extends Component {} export function Portfolio({ className = "", testId = "portfolio" }: PortfolioProps) { return ( <AccordionGroup className={`overflow-hidden ${className}`} testId={testId}> {projects.map((project, index) => ( <Accordion title={project.title} key={`${index}-${project}`} headingSize="h2"> <ProjectCard project={project} /> </Accordion> ))} </AccordionGroup> ) }
AccordionGroup.tsx - Tujuan AccordionGroup adalah untuk membenarkan hanya satu anak Accordion dibuka pada satu masa. Jika Accordion tidak berada dalam AccordionGroup, ia boleh dibuka dan ditutup secara berasingan.
"use client" import React, { Children, ReactElement, cloneElement, isValidElement, useState } from "react" import { AccordionProps } from "@/components/atoms/Accordion" import { Component } from "@/shared/types" interface AccordionGroupProps extends Component { children: ReactElement<AccordionProps>[] } export function AccordionGroup({ children, className = "", testId = "accordion-group", }: AccordionGroupProps) { const [activeAccordion, setActiveAccordion] = useState<number | null>(null) function handleAccordionToggle(index: number) { setActiveAccordion((prevIndex) => (prevIndex === index ? null : index)) } return ( <div className={className} data-testid={testId}> {Children.map(children, (child, index) => isValidElement(child) ? cloneElement(child, { onClick: () => handleAccordionToggle(index), isActive: activeAccordion === index, children: child.props.children, title: child.props.title, }) : child )} </div> ) }
akordion.tsx
"use client" import { Component } from "@/shared/types" import React, { MutableRefObject, ReactNode, RefObject, useEffect, useRef, useState } from "react" import { Heading } from "@/components/atoms/Heading" export interface AccordionProps extends Component { title: string children: ReactNode isActive?: boolean onClick?: () => void headingSize?: "h1" | "h2" } export function Accordion({ className = "", title, children, isActive, onClick, headingSize = "h1", testId = "Accordion", }: AccordionProps) { const [isOpen, setIsOpen] = useState(false) const [height, setHeight] = useState("0px") const contentHeight = useRef(null) as MutableRefObject<HTMLElement | null> useEffect(() => { if (isActive === undefined) return isActive ? setHeight(`${contentHeight.current?.scrollHeight}px`) : setHeight("0px") }, [isActive]) function handleToggle() { if (!contentHeight?.current) return setIsOpen((prevState) => !prevState) setHeight(isOpen ? "0px" : `${contentHeight.current.scrollHeight}px`) if (onClick) onClick() } return ( <div className={`w-full text-lg font-medium text-left focus:outline-none ${className}`}> <button onClick={handleToggle} data-testid={testId}> <Heading className="flex items-center justify-between" color={isActive ? "text-blue-200" : "text-white"} level={headingSize} > {title} </Heading> </button> <div className={`overflow-hidden transition-max-height duration-250 ease-in-out`} ref={contentHeight as RefObject<HTMLDivElement>} style={{ maxHeight: height }} > <div className="pt-2 pb-4">{children}</div> </div> </div> ) }
Kad Projek.tsx
import Image from "next/image" import { Card } from "@/components/atoms/Card" import { Children, Component, Project } from "@/shared/types" import { Subheading } from "@/components/atoms/Subheading" import { Tag } from "@/components/atoms/Tag" import { Text } from "@/components/atoms/Text" interface ProjectCardProps extends Component { project: Project } export function ProjectCard({ className = "", testId = "project-card", project, }: ProjectCardProps) { const { title, description, coverImage: { src, alt, height, width }, tags, } = project return ( <Card className={`flex min-h-[300px] ${className}`} data-testid={testId}> <div className="w-1/2"> <CoverImage className="relative w-full h-full mb-4 -mx-6-mt-6"> <Image className="absolute inset-0 object-cover object-center w-full h-full rounded-l-md" src={src} alt={alt} width={parseInt(width)} height={parseInt(height)} loading="eager" /> </CoverImage> </div> <div className="w-1/2 p-4 px-8 text-left"> <Subheading className="text-3xl font-bold" color="text-black"> {title} </Subheading> <Tags className="flex flex-wrap pb-2"> {tags.map((tag, index) => ( <Tag className="mt-2 mr-2" key={`${index}-${tag}`} text={tag} /> ))} </Tags> <Text color="text-black" className="text-sm"> {description} </Text> </div> </Card> ) } function CoverImage({ children, className }: Children) { return <div className={className}>{children}</div> } function Tags({ children, className }: Children) { return <div className={className}>{children}</div> }
Sebarang bantuan akan sangat dihargai, terima kasih!
Analisis masalah:
TL;DR: Akordion induk perlu mengetahui tentang perubahan ini supaya ia boleh menyesuaikan ketinggiannya dengan sewajarnya.
Saya rasa anda mungkin menggunakan
amiut/accordionify
seperti yang ditunjukkan melalui "Mencipta React Accordions yang ringan" daripada Amin A. Rezapour.Ini adalah satu-satunya projek yang saya temui yang menggunakan
AccordionGroup
.Struktur akordion bersarang dalam aplikasi melibatkan hubungan ibu bapa-anak, di mana ketinggian akordion kanak-kanak berubah secara dinamik bergantung pada sama ada ia mengembang atau runtuh.
Ini boleh digambarkan oleh
Portfolio.tsx
anda, di mana komponenPortfolio.tsx
来说明,其中AccordionGroup
组件包含多个基于创建的
数组。这些Accordion
组件项目Accordion
mengandungi berbilang tatasusunanAccordion
item komponen yang dibuat berdasarkan. Komponen
Accordion
ini ialah akordion "kanak-kanak" yang disebut:Setiap sub
Accordion
都包含一个显示项目详细信息的ProjectCard
。当用户单击Accordion
(或“项目”)时,它会展开以显示ProjectCard
.Di sinilah perubahan ketinggian berlaku; akordion akan mengembang atau runtuh berdasarkan interaksi pengguna, mengubah ketinggiannya secara dinamik.
Ketinggian dinamik diuruskan dalam
Accordion.tsx
:apabila dipanggil
handleToggle
函数时,它会检查手风琴当前是否打开(isOpen
). Jika ya, ketinggian ditetapkan kepada "0px" (iaitu akordion runtuh). Jika tidak dibuka, ketinggian ditetapkan kepada ketinggian tatal kandungan (iaitu akordion dikembangkan).Perubahan ketinggian dinamik akordion kanak-kanak ini adalah bahagian utama masalah anda. Akordion induk perlu mengetahui tentang perubahan ini supaya ia boleh menyesuaikan ketinggiannya dengan sewajarnya.
Saya melihat dalam yang sama
Accordion.tsx
:Ketinggian akordion adalah berdasarkan
isActive
属性设置的,它表示手风琴当前是否打开。如果打开,则高度设置为手风琴内容的滚动高度(有效展开手风琴),如果未激活,则高度设置为0px
(akordion lipat).Walau bagaimanapun, sementara kesan ini melaraskan ketinggian setiap akordion dengan betul berdasarkan keadaannya sendiri
isActive
, ia tidak mengambil kira perubahan dalam ketinggian akordion kanak-kanak.Apabila akordion bersarang (anak) menukar ketinggiannya (disebabkan pengembangan atau keruntuhan), ketinggian akordion induk tidak dikira semula dan oleh itu tidak menyesuaikan diri agar sesuai dengan ketinggian baru anak-anak ibu bapa.
Dalam erti kata lain, apabila ketinggian akordion kanak-kanak berubah, akordion ibu bapa tidak tahu bahawa ia perlu membuat semula dan menyesuaikan ketinggiannya. Kekurangan pemaparan semula apabila akordion bersarang dibesarkan atau diruntuhkan menyebabkan akordion induk tidak memaparkan ketinggian yang betul.
Penyelesaian yang mungkin
TL;DR: Penyelesaiannya melibatkan menyedarkan ibu bapa tentang perubahan ketinggian akordion anak supaya ia boleh menyesuaikan ketinggiannya sendiri dengan sewajarnya.
(“Reaksi: Salah satu teknik yang disebut dalam pemaparan semula komponen Force | 4 cara mudah ” oleh Josip Miskovic)
Harta
Accordion
组件可以受益于回调函数 prop,该函数在其高度发生变化时被调用,例如onHeightChange
。然后,在Portfolio
组件中,您可以通过将新的回调函数传递给Accordion
组件来将此高度更改向上传播到Homepage
组件,利用onHeightChange
anda.手风琴.tsx
:Kemudian ubah suai komponen Portfolio anda untuk menyebarkan peristiwa perubahan ketinggian:
Akhir sekali, anda boleh menambah kunci pada akordion Portfolio pada halaman utama yang akan berubah apabila peristiwa perubahan ketinggian dicetuskan. Ini akan menyebabkan akordion untuk membuat semula:
Dengan cara ini, anda akan memaksa komponen induk Accordion untuk membuat semula apabila ketinggian anak Accordion berubah.
Anda tahu, pelaksanaan di sini agak mencabar kerana apabila anda ingin mengemas kini ketinggian akordion datuk dan nenek daripada akordion anaknya, anda tidak boleh benar-benar tahu dari sana akordion datuk dan nenek yang sepadan yang anda ingin kemas kini melainkan anda menyerahkan prop kepada akordion datuk nenek, dan hantar prop kepada komponen perantaraan (cth.,
Portfolio
, induk akordion kanak-kanak) supaya ia boleh membiaknya kepada akordion anaknya.Dengan melakukan ini, kami boleh membenarkan datuk dan nenek dan anak-anak akordion berkomunikasi dalam beberapa cara.
Mungkin ini bukan penyelesaian terbaik yang boleh anda temui, tetapi malangnya saya tidak dapat memikirkan penyelesaian yang lebih baik.
Jadi untuk mengimbas kembali: ideanya adalah untuk mencipta keadaan di peringkat atas untuk memegang rujukan kepada ketinggian setiap akordion induk, jadi ia adalah tatasusunan di mana panjang ditetapkan "secara manual", yang menjadikannya agak hodoh, tetapi Jika anda perlu menggunakan tatasusunan data untuk memaparkan komponen anda secara dinamik maka ini tidak menjadi masalah, kerana kita akan mengetahui kemudian, kita juga akan melihat batasan penyelesaian.
Penyelesaian:
Sekarang kita akan menggunakan pembetulan paling mudah dan paling mudah yang sesuai untuk perkara yang disertakan dalam soalan.
Seperti yang dinyatakan di atas, mula-mula kita cipta keadaan dalam komponen Halaman Utama:
Selepas mencipta keadaan tatasusunan di peringkat atas, kini, kami menghantar fungsi tetapan keadaan kepada setiap komponen Akordion
setHeights
、索引indexx
以及相应的heightheightParent
jika ia adalah Akordion indukNOTA: Sifat
indexx
属性和传递给中间组件(Portfolio)的indexx
yang diserahkan kepada induk sepatutnya mempunyai nilai yang sama menunjukkan indeks yang sepadan, yang sebenarnya merupakan kunci kepada penyelesaian.Namakan "indexx" dengan dua "x" di dalamnya untuk mengelakkan konflik kemudian.
Kemudian, hantar prop yang diterima ini kepada akordion kanak-kanak dari komponen tengah:
Kini, daripada komponen Akordion anak anda, anda boleh menggunakan harta
indexx
yang diluluskan untuk mengemas kini ketinggian induk Accordion yang sepadan dalam keadaan Halaman Utama, jadi apabila kami mengemas kini ketinggian anak, kami juga mengemas kini ketinggian indukAkhir sekali, apabila anda menentukan ketinggian Akordion anda boleh menyemak sama ada ia menerima
heightParent
作为 props,以便我们知道它是父级,这样,我们让 Accordion 组件使用heightParent
作为maxHeight
而不是它自己的状态height
(如果它是父状态),这就是忽略更新height
状态的原因当它是打开的父 Accordion 时,因此,我们必须更改maxHeight
cara harta itu ditetapkan, kini ia harus bergantung pada sifat Akordion:Jika anda mahukan Accordion induk hanya gunakan keadaannya
height
作为maxHeight
dan pastikan kodnya sama, ia lebih masuk akalAnda masih boleh menjalankannya dengan menambah status
useEffect
并确保其仅在更新并定义接收到的heightParent
属性时运行来执行此操作,我们这样做确保代码仅在父手风琴应更新其height
dalam komponen Akordion:Seperti yang dinyatakan di atas, yang ini lebih masuk akal dan juga paling cantik, tetapi saya masih lebih suka yang pertama kerana ia lebih ringkas dan juga menjimatkan render tambahan.
Pemprosesan data dinamik:
Jika kami menyimpan data dalam tatasusunan dan ingin memaparkan komponen kami berdasarkan ini, kami boleh melakukan ini:
Anda boleh perasan bahawa kami perlu menentukan
key
,以便我们可以使用它而不是indexx
,但您知道key
> harta dalam akordion induk dan kami tidak mahu mengacaukannya, harap anda fahamKeterbatasan:
Jelas sekali penyelesaian ini hanya berfungsi untuk satu tahap, jadi jika sub-akordion itu sendiri menjadi sub-akordion anda perlu membungkusnya semula, tetapi jika saya faham apa yang anda lakukan, anda mungkin tidak akan menghadapi ini, Kerana dengan pelaksanaan anda kanak-kanak Accordion harus menunjukkan item itu, tetapi siapa tahu mungkin suatu hari nanti anda perlu mengembalikannya lagi anak Accordion, itulah sebabnya saya fikir cadangan saya adalah penyelesaian dan bukan penyelesaian terbaik.
Seperti yang saya katakan, ini mungkin bukan penyelesaian terbaik, tetapi sejujurnya, terutamanya untuk pelaksanaan ini, Saya tidak fikir penyelesaian kerja pelbagai peringkat sedemikian wujud, sila buktikan saya salah, saya mengikuti siaran itu .