I am studying Typescript with React I have Team Page and a slider component, the component works just fine but I want to define the type of children.
TeamPage
import react, { Fragment, useEffect, useState } from "react";
import { Container, Row, Button, Col } from "react-bootstrap";
import Slider from "./components/Slider";
const SliderProps = {
zoomFactor: 30, // How much the image should zoom on hover in percent
slideMargin: 10, // Margin on each side of slides
maxVisibleSlides: 5,
pageTransition: 500, // Transition time when flipping pages
};
export interface PlayerList {
players: {
name: string;
username: string;
}[];
}
const TeamPage: React.FC = () => {
const playerList = {
players: [
{
name: "Exon",
username: "p_exon",
},
{
name: "Nolan",
username: "p_nolan",
},
{
name: "T Mac",
username: "p_tmac",
},
{
name: "Warren",
username: "p_warren",
},
],
};
if (playerList.players.length < 1) return <div>Loading </div>;
return (
<Container fluid p-0>
<Row>
<Col md={{ span: 7, offset: 2 }}>
<h2>Player List</h2>
</Col>
</Row>
<Row>
<Col>
<Slider {...SliderProps}>
{playerList.players.map((PlayerList) => (
<div key={PlayerList.name}>
<img
src="https://st.depositphotos.com/2101611/3925/v/600/depositphotos_39258143-stock-illustration-businessman-avatar-profile-picture.jpg"
alt="xd"
/>
<>{PlayerList.name}</>
</div>
))}
</Slider>
</Col>
</Row>
</Container>
);
};
export default TeamPage;
And a Slider component
import React, { useState, useEffect, useRef } from "react";
import SliderItem from "./SliderItem";
import { StyledSliderWrapper, StyledSlider } from "./SliderStyles";
type SliderProps = {
children?: any;
zoomFactor: number;
slideMargin: number;
maxVisibleSlides: number;
pageTransition: number;
};
const numberOfSlides = (maxVisibleSlides: number, windowWidth: number) => {
if (windowWidth > 1200) return maxVisibleSlides;
if (windowWidth > 992) return 4;
if (windowWidth > 768) return 3;
return 2;
};
const Slider: React.FC<SliderProps> = ({
children,
zoomFactor,
slideMargin,
maxVisibleSlides,
pageTransition,
}) => {
const [currentPage, setCurrentPage] = useState(0);
const [transformValue, setTransformValue] = useState(`-${zoomFactor / 2}%`);
const [scrollSize, setScrollSize] = useState(0);
const sliderRef = useRef<HTMLElement>(null!);
const visibleSlides = numberOfSlides(maxVisibleSlides, scrollSize);
const totalPages: number = Math.ceil(children.length / visibleSlides) - 1;
useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
setScrollSize(entries[0].contentRect.width);
});
resizeObserver.observe(sliderRef.current);
}, [sliderRef]);
useEffect(() => {
if (sliderRef && sliderRef.current) {
if (currentPage > totalPages) setCurrentPage(totalPages);
sliderRef.current.style.transform = `translate3D(-${
currentPage * scrollSize
}px, 0, 0)`;
}
}, [sliderRef, currentPage, scrollSize, totalPages]);
const disableHoverEffect = () => {
if (sliderRef.current) sliderRef.current.style.pointerEvents = "none";
setTimeout(() => {
if (sliderRef.current) sliderRef.current.style.pointerEvents = "all";
}, pageTransition);
};
const handleSlideMove = (forward: boolean) => {
disableHoverEffect();
setCurrentPage(currentPage (forward ? 1 : -1));
if (sliderRef.current)
sliderRef.current.style.transform = `translate3D(-${
(currentPage (forward ? 1 : -1)) * scrollSize
}px, 0, 0)`;
};
const handleMouseOver = (id: number) => {
if (id % visibleSlides === 1) setTransformValue("0%");
if (id % visibleSlides === 0) setTransformValue(`-${zoomFactor}%`);
};
const handleMouseOut = () => {
setTransformValue(`-${zoomFactor / 2}%`);
};
const assignSlideClass = (index: number, visibleSlides2: number) => {
const classes = ["right", "left"];
return classes[index % visibleSlides2] || "";
};
return (
<StyledSliderWrapper zoomFactor={zoomFactor} visibleSlides={visibleSlides}>
<StyledSlider
visibleSlides={visibleSlides}
transformValue={transformValue}
zoomFactor={zoomFactor}
slideMargin={slideMargin}
pageTransition={pageTransition}
ref={sliderRef}
>
{children.map((child: any, i: any) => (
<SliderItem
key={i}
slideMargin={slideMargin}
visibleSlides={visibleSlides}
zoomFactor={zoomFactor}
slideClass={assignSlideClass(i 1, visibleSlides)}
id={i 1}
callback={handleMouseOver}
callbackOut={handleMouseOut}
>
{child}
</SliderItem>
))}
</StyledSlider>
{currentPage > 0 && (
<div className="button-wrapper back">
<button
className="button back"
onClick={() => handleSlideMove(false)}
>
‹
</button>
</div>
)}
{currentPage !== totalPages && (
<div className="button-wrapper forward">
<button
className="button forward"
onClick={() => handleSlideMove(true)}
>
›
</button>
</div>
)}
</StyledSliderWrapper>
);
};
export default Slider;
I want to clarify that the component works, I have tried React.ReactNode and reactElement type but I seem to be unable to achieve it, I also tried defining children as PlayerList interface but when I use the ...SliderProps on teampage <Slider {...SliderProps}>
the types don't match and I get this error.
Property 'players' is missing in type 'Element[]' but required in type 'PlayerList'.
and if the type of children change I think this lines on the slider component
const totalPages: number = Math.ceil(children.length / visibleSlides) - 1;
And this lines on the slider component have to change
{children.map((child: any, i: any) => (
<SliderItem
key={i}
slideMargin={slideMargin}
visibleSlides={visibleSlides}
zoomFactor={zoomFactor}
slideClass={assignSlideClass(i 1, visibleSlides)}
id={i 1}
callback={handleMouseOver}
callbackOut={handleMouseOut}
>
{child}
</SliderItem>
I'm also looking to avoid and object possibly null or undefined. would appreciate an explanation.
CodePudding user response:
children
should indeed be declared as children?: React.ReactNode;
.
type SliderProps = {
children?: React.ReactNode;
zoomFactor: number;
slideMargin: number;
maxVisibleSlides: number;
pageTransition: number;
};
Now, to get the given count and map into each child you have to use React.Children
utility class. It's going to be like the following to get the count:
const totalPages: number = Math.ceil(React.Children.count(children) / visibleSlides) - 1;
And to map:
{ React.Children.map(children, (child, i) => (
<SliderItem
key={i}
slideMargin={slideMargin}
visibleSlides={visibleSlides}
zoomFactor={zoomFactor}
slideClass={assignSlideClass(i 1, visibleSlides)}
id={i 1}
callback={handleMouseOver}
callbackOut={handleMouseOut}
>
{child}
</SliderItem>
))}