javascript - Span-element visas inte inuti div i React-komponent med Tailwind CSS


Jag arbetar på en React-komponent som använder Tailwind CSS för styling, och jag stöter på ett problem där ett element inte visas inuti ett . Jag försöker skapa en videokarussell där varje video har en motsvarande indikator, och indikatorn inkluderar ett element för framsteg. Men elementen visas inte inuti sina föräldra s.

Jag har försökt med några Youtube-handledning för att lära mig mer om React och hur man korrekt delar upp och skapar moderna layouter, vilket ledde mig till denna speciella video: https://www.youtube.com/watch?v=RbxHZwFtRT4&list=PLoBCJPS0hA0EMxY9gBirKArw5Ko_5UsjP

Som ett sätt att också öva på TypeScript bestämde jag mig för att göra det med .tsx istället för .jsx, vilket faktiskt gjorde vissa saker mer komplicerade.

Just nu sitter jag fast med ett problem som jag tror härrör från användningen av typerna och som används för att initiera ett useState inom Carousel-komponenten.

Saken är att jag verkligen inte förstår varför det händer. Har bokstavligen ingen aning.

     const videoDivRef = useRef<(HTMLDivElement | null)[]>([]);
 const videoSpanRef = useRef<(HTMLSpanElement | null)[]>([]);
    
      const [video, setVideo] = useState({
        isEnd: false,
        startPlay: false,
        videoId: 0,
        isLast: false,
        isPlaying: false,
      });
      const { isEnd, startPlay, videoId, isLast, isPlaying } = video;
{videoRef.current.map((_, index) => (
            <div
              key={index}
              ref={(el) => {
                videoDivRef.current[index] = el;
              }}
              className="mx-2 w-3 h-3 bg-gray-200 rounded-full relative cursor-pointer inline-block"
            >
</div>

Så den här div:en är tänkt att vara rullningslisten som senare kommer att interagera med en absolut positionerad, eller snarare en massa, spans som flyter i den. Nu i Youtube-tutorialen använder författaren en span tillsammans med DivRef, vilket eftersom jag använder Div-typen i sin useState helt enkelt inte skulle fungera. Tänkte att jag lika gärna kunde göra den till en vanlig div och se till att göra den inline så att den beter sig som en span. Och det fungerade. Det här var resultatet, vilket var förväntat.

https://postimg.cc/YjdFYGYw

(Kunde inte ladda upp bilden, vet inte varför)

och allt är bra! Tills det kommer tid att faktiskt lägga till de flytande spannen ovanpå det som senare kommer att användas för animations-effekterna.

När jag lägger till spannarna:

  {videoRef.current.map((_, index) => (
            <div
              key={index}
              ref={(el) => {
                videoDivRef.current[index] = el;
              }}
              className="mx-2 w-3 h-3 bg-gray-200 rounded-full relative cursor-pointer inline-block"
            >
                 <span 
                className="absolute top-0 left-0 h-full w-full rounded-full bg-blue-500"
                ref={(el) => {
                  videoSpanRef.current[index] = el;
                }}
              >
              </span>
            </div>

Det blir bara: https://postimg.cc/WdkqB5K2

Nu förstår jag inte varför detta händer. Det ska ju flyta och eftersom det närmaste relaterade elementet är div:en som det ligger i. Det ska ju flyta ovanpå den.. eller? Jag vill bara förstå bättre varför det händer. Eftersom jag använder .tsx och handledningen använder .jsx måste jag tro att det har något att göra med det. Men om så är fallet, hur kan jag fixa det?

Det här är hela koden om du är nyfiken: https://github.com/yzkael/modernUIUX

Tack för din tid. All feedback tas verkligen emot med uppskattning!

Svar #1

Problemet du står inför beror på att du förväntar dig att ändringar i refs ska orsaka en återrendering av din komponent. Refs är beständiga datastrukturer som inte orsakar återrendering när de ändras. Syftet med refs är att hålla reda på en datastruktur, vanligtvis ett HTML-element, under komponentens hela livscykel. Refs är inte reaktiva, vilket betyder att uppdatering av dem inte uppdaterar användargränssnittet.

Observera att det initiala värdet på din ref är en tom array, och videoelementen läggs till i listan endast när komponenten renderas initialt. Det initiala värdet är satt som [], och eftersom att lägga till element i listan inte orsakar en omrendering, visar ditt gränssnitt fortfarande en tom lista.

Det du behöver är att göra din karusellmeny reaktiv mot videoelementen, eftersom den för närvarande inte är det.

En av de möjliga lösningarna skulle vara att ha ett ref-objekt för att hålla videoreferenser och ett state-objekt som tilldelas ref:ens värden efter att de har tilldelats referenserna, vilket innebär efter den första renderingen. Använd detta state för att visa ditt användargränssnitt. State är reaktivt, och att ändra state kommer att uppdatera användargränssnittet därefter, vilket inte är fallet med ref.

  // define a state for your video els
  const [videoEls, setVideoEls] = useState<(HTMLVideoElement | null)[]>([]);

  // apply an effect AFTER the first render. When this effect is called, refs are already assigned, so you can use them.
  useEffect(() => {
    // after the state is updated, the component will re-render
    setVideoEls(videoRef.current)
  }, [])

  // iterate over the state, which is reactive, rather than the ref, which is not reactive
  // on first render, no spans will be displayed, and as soon as the state is assigned the video elements, this piece of UI will be updated accordingly
  {videoEls.map((_, index) => (

Detta kommer att visa din meny korrekt.

Efter att ha gjort det kommer du förmodligen vilja reagera på klick på span-elementen. För det behöver du några "onClick"-händelser och ett "selectedVideo"-tillstånd, som till exempel kan innehålla ett element eller en nyckel. Hur du än implementerar detta, se alltid till att du förlitar dig på reaktiv data för att återspegla förändringar efter att användaren har interagerat med din app.

Obs: ett ännu enklare tillvägagångssätt skulle vara att inte alls förlita sig på refs, utan på hightlightsSlides. Det verkar som om du inte behöver refs här. Du skulle kunna iterera över hightlightsSlides för att visa dina spans på ett enklare sätt, och vid den första renderingen.