I'm working on a React component that uses Tailwind CSS for styling, and I'm encountering an issue where a element is not displaying inside a . I'm trying to create a video carousel where each video has a corresponding indicator, and the indicator includes a element for progress. However, the elements are not showing up inside their parent s.
I have been trying some Youtube Tutorial to learn more about React and how to properly divide and create modern Layouts which lead me to this particular video: https://www.youtube.com/watch?v=RbxHZwFtRT4&list=PLoBCJPS0hA0EMxY9gBirKArw5Ko_5UsjP
As a way to also practice TypeScript I decided to do it using .tsx instead of .jsx which did make some things more complex.
Right now I am stuck on a problem that I believe stems from the use of the and types that are used to initialize a useState within the Carousel Component.
The thing is I don't really understand why its happening. Literally no clue.
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>
So this div is supposed to be the scrollbar that will later interact, with a absolute positioned,or rather a bunch of, spans floating in it. Now in the Youtube Tutorial the author uses a span that with the DivRef, which since I am using the Div Type on its useState just wouldn't work. Figured I'd just make it a regular div and make sure to make it Inline so it behaves like a span. And It did work. this was the result, which was expected.
(Couldn't upload the image don't know why)
and its all good! Untill it comes the time to actually add the floating spans on top of it that will be later used for the animation effects.
Once I add the spans:
{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>
It just turns into: https://postimg.cc/WdkqB5K2
Now I don't understand why does this happend. Its supposed to be floating and since the closest relative element its the div is located in. Its supposed to be floating on top of it.. no? I just want to understand better why its happening. Since I am using .tsx and the tutorial its using .jsx I must believe it has to do something with it. But if so. How can I fix it?
This is the full code in case you are curious: https://github.com/yzkael/modernUIUX
Thank you for your time. Any feedback is really appreciated!
The problem you are facing is due to the fact you're expecting refs changes to cause a re-render of your component. Refs are persistent data structures that do not cause re-render when modified. The point of refs is to keep track of a data structure, typically an HTML element, throughout the component's lifecycle. Refs are not reactive, which means updating them doesn't update the UI.
Note that the initial value of your ref is an empty array, and the video elements are added to the list only when the component initially renders. The initial value is set as []
, and since adding elements to the list does not cause a re-render, your interface still displays an empty list.
What you need is to make your carousel menu reactive to the video elements, because currently it is not.
One of the possible solutions would be to have a ref object to hold videos refs, and a state object that's assigned the ref's values after they are assigned to the refs, which means after the first render. Use this state to display your UI. The state is reactive, and changing the state will update the UI accordingly, which is not the case with the 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) => (
This will display your menu correctly.
After having done that you will probably want to react to clicks on the spans. For that you'll need some "onClick" events and a "selectedVideo" state, which could contain an element or a key, for example. However you implement this, always make sure that you're relying on reactive data to reflect changes after the user interacts with your app.
Note: an even simpler approach would be to not rely on refs at all, but on hightlightsSlides
. It seems like you don't need refs here. You could iterate over hightlightsSlides
to display your spans more simply, and at the first render.