Home > OS >  How to define type any of property children? typescript
How to define type any of property children? typescript

Time:08-24

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)}
          >
            &#8249;
          </button>
        </div>
      )}
      {currentPage !== totalPages && (
        <div className="button-wrapper forward">
          <button
            className="button forward"
            onClick={() => handleSlideMove(true)}
          >
            &#8250;
          </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>
))}
  • Related