React how to repopulate values when back button clicked - reactjs

In react js i get value from api then i used component to populated vale after back button clicked i don`t want to remove my value i want to my current data that work in text filed but it not work on
select it always remove it value.it show error name not defined.
i want repopulate select when clicked back button
import React from 'react';
import styled from 'styled-components';
import Select from 'react-select';
import { useNavigate } from 'react-router-dom';
import { QsrQoreApi, Eats365, StoreHub } from './Sources';
import SourceStoreSelection from './SourceStoreSelection';
import svgs from '../../shared/svgs';
import { Modal, withAuth } from '../../shared';
const StyledSourceSettings = styled.div`
& .header {
display: flex;
height: 5.2rem;
align-items: center;
border-bottom: 0.1rem solid #edf2f7;
}
& .back {
height: 2rem;
width: 2rem;
margin-left: 2rem;
}
& .title {
margin-left: max(1.5rem, calc(((100vw - 21rem) / 2) - 4rem));
width: 21rem;
height: 2rem;
font-size: 1.6rem;
font-weight: 700;
line-height: 2rem;
text-align: center;
color: #2d3748;
}
& .source-form {
padding: 2rem;
display: grid;
grid-template-columns: 1fr;
}
& .login {
width: 28rem;
margin: 0 auto 0.4rem auto;
font-size: 1.6rem;
font-weight: 700;
color: #2d3748;
}
& .form-input {
margin: 1.5rem auto 0 auto;
width: 28rem;
font-size: 1.6rem;
color: #4a5568;
}
& .form-input > .input {
width: 28rem;
margin: 0.5rem 0 0 0;
height: 3.6rem;
font-size: 1.6rem;
border: 0.1rem solid #a0aec0;
border-radius: 0.4rem;
color: #4a5568;
padding: 1rem;
}
& .form-input > .select {
margin: 0.5rem 0 0 0;
}
& .buffer-152px {
height: 15.2rem;
}
& .buffer-91px {
height: 9.1rem;
}
& .selected-store {
width: 28rem;
height: 2rem;
margin: 2rem auto 0 auto;
font-size: 1.6rem;
font-weight: 700;
color: #2d3748;
}
& .selected-store-detail {
display: flex;
height: 5.1rem;
margin: 0 auto;
align-items: center;
border-bottom: 0.1rem solid #edf2f7;
font-size: 1.6rem;
color: #4a5568;
}
& .selected-store-detail > .map {
height: 2rem;
width: 2rem;
}
& .selected-store-detail > .name {
height: 2rem;
width: 22rem;
margin-left: 1rem;
}
& .selected-store-detail > .forward {
height: 2rem;
width: 2rem;
margin-left: 1rem;
}
& .next-btn {
width: 28rem;
height: 3.6rem;
margin: auto;
margin-top: max(5.3rem, calc(100vh - 47.1rem));
border: 0.1rem solid #cbd5e0;
border-radius: 0.4rem;
background: #ffffff;
box-shadow: 0rem 0.2rem 0.4rem rgba(24, 39, 75, 0.12);
padding: 1rem;
font-size: 1.6rem;
font-weight: 600;
color: #4a5568;
}
& .next-btn:disabled,
& .next-btn[disabled] {
box-shadow: none;
color: #cbd5e0;
}
`;
const sourceOpts = [
{ value: 'QsrQoreApi', label: 'QsrQoreApi' },
{ value: 'Eats365', label: 'Eats365' },
{ value: 'StoreHub', label: 'StoreHub' },
];
const sourceSelectStyles = {
control: (provided) => ({
...provided,
width: '28rem',
minHeight: '3.6rem',
height: '3.6rem',
fontSize: '1.6rem',
border: '0.1rem solid #a0aec0',
borderRadius: '0.4rem',
color: '#4a5568',
}),
indicatorSeparator: () => {},
singleValue: (provided) => ({
...provided,
color: '#4a5568',
}),
valueContainer: (provided) => ({
...provided,
padding: '0.2rem 0.8rem',
}),
placeholder: (provided) => ({
...provided,
margin: 'auto 0.2rem',
}),
input: (provided) => ({
...provided,
margin: '0.2rem',
padding: '0.2rem auto',
}),
dropdownIndicator: (provided) => ({
...provided,
'& svg': {
height: '2rem',
width: '2rem',
},
}),
indicatorsContainer: (provided) => ({
...provided,
'& > div': {
padding: '0.8rem',
},
}),
menu: (provided) => ({
...provided,
borderRadius: '0.4rem',
margin: '0.8rem auto',
}),
menuList: (provided) => ({
...provided,
maxHeight: '30rem',
padding: '0.4rem 0',
}),
option: (provided) => ({
...provided,
padding: '0.8rem 1.2rem',
}),
};
function AddSource(props) {
const { setForm, store, setStore , source} = props;
const [sourceAdditionalInfos, setSourceAdditionalInfos] = React.useState({});
const [sourceStore, setSourceStore] = React.useState(null);
const [showModal, setShowModal] = React.useState(false);
const navigate = useNavigate();
React.useEffect(() => {
if(store.source){
const infos = store.source.additionalInfos.reduce((result, { key, value }) => {
const temp = result;
temp[key] = value;
return temp;
}, {});
setSourceAdditionalInfos(infos);
}
},[]);
React.useEffect(() => {
if (store.source) {
const infospos = store.source.type
const value = infospos;
setSourceStore(value);
}
},[]);
const onBackClick = () => {
navigate(-1);
};
const onSourceSelectChange = (source) => {
if (source) {
setStore({ ...store, source: { type: source.value } });
setSourceStore(null);
}
};
const handleChange = (e) => setSourceAdditionalInfos({ ...sourceAdditionalInfos, [e.target.id]: e.target.value });
const onSubmit = (e) => {
e.preventDefault();
if (!sourceStore) setShowModal(true);
else {
const addInfos = Object.entries(sourceAdditionalInfos).map(([k, v]) => ({ key: k, value: v }));
let st = {
...store,
source: {
...store.source,
...sourceStore.value,
additionalInfos: addInfos.concat(sourceStore.value.additionalInfos),
},
};
setStore(st);
setForm('addStore');
}
};
let renderSource = <div className="buffer-152px" />;
if (store.source) {
switch (store.source.type) {
case 'QsrQoreApi':
renderSource = (
<QsrQoreApi
handleChange={handleChange}
sourceAdditionalInfos={sourceAdditionalInfos}
disableInput={sourceStore}
/>
);
break;
case 'Eats365':
renderSource = (
<Eats365
handleChange={handleChange}
sourceAdditionalInfos={sourceAdditionalInfos}
disableInput={sourceStore}
/>
);
break;
case 'StoreHub':
renderSource = (
<StoreHub
handleChange={handleChange}
sourceAdditionalInfos={sourceAdditionalInfos}
disableInput={sourceStore}
/>
);
break;
default:
renderSource = <div className="buffer-152px" />;
break;
}
}
let renderSelectedSourceStore = '';
if (sourceStore) {
renderSelectedSourceStore = (
<>
<div className="selected-store">Store selection</div>
<div className="selected-store-detail" onClick={() => setShowModal(true)}>
<img src={svgs.map} className="map" alt="map" />
<div className="name">{sourceStore.value.name}</div>
<img src={svgs.forward} className="forward" alt="forward" />
</div>
</>
);
} else {
renderSelectedSourceStore = <div className="buffer-91px" />;
}
return (
<StyledSourceSettings>
<div className="header">
<img src={svgs.back} className="back" alt="back" onClick={onBackClick} />
<div className="title">POS System</div>
</div>
<form className="source-form" onSubmit={onSubmit}>
<div className="login">Login credentials</div>
<label className="form-input" htmlFor="type">
<div className="label">POS System</div>
<div className="select">
<Select
options={sourceOpts}
styles={sourceSelectStyles}
placeholder="Choose one"
onChange={onSourceSelectChange}
inputId="type"
isDisabled={sourceStore}
/>
</div>
</label>
{renderSource}
{renderSelectedSourceStore}
<input className="next-btn" type="submit" value="NEXT" disabled={!(store.source && store.source.type)} />
</form>
<Modal onClose={() => setShowModal(false)} open={showModal}>
<SourceStoreSelection
onSubmitted={() => setShowModal(false)}
onClosed={() => setShowModal(false)}
store={store}
sourceAdditionalInfos={sourceAdditionalInfos}
sourceStore={sourceStore}
setSourceStore={setSourceStore}
/>
</Modal>
</StyledSourceSettings>
);
}
export default withAuth(AddSource);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
enter image description here

Try to change it to:
{sourceStore?.value?.name}

Related

In React, how to setState a Data by using hook useState?

I am just getting started learning React Functional component and styled- component. I tried to setState detailePageData
const const [marketPriceData, setMarketPriceData] = useState([]);
and I tried to set up the marketPriceData by using JSON file.
useEffect(() => {
fetch(`/data/detailPageData.json`)
.then(res => res.json())
.then(data => setMarketPriceData(data.result.order_history));
}, []);
When I console.log marketPriceData. I got this
In here, I only need the price in each array. So, when I console.log(marketPriceData[0].price); I got 378000 what I expected.
Here is my main question, I only need each array price such as; 378000,341000,388000, etc.
I think I have to edit this part
useEffect(() => {
fetch(`/data/detailPageData.json`)
.then(res => res.json())
.then(data => setMarketPriceData(data.result.order_history));
}, []);
First, I think "Can I use foreach method? or for loop..?" I am not sure how to setup the logic to store the setMarketPriceData. Could you please help me with how to get the price what I expected?
Just in case, I will leave the whole code,
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { FaRegArrowAltCircleDown } from 'react-icons/fa';
import { Line } from 'react-chartjs-2';
import { Modal } from './components/Modal';
import TransitionHistoryModalStyling from '../ProductDetail/components/TransitionHistoryModalStyling';
import DropDownMenu from '../ProductDetail/components/DropDownMenu';
const options = {
scales: {
y: {
beginAtZero: true,
},
},
};
export default function MarketPrice() {
const [showModal, setShowModal] = useState(false);
const [marketPriceData, setMarketPriceData] = useState([]);
// https://github.com/reactchartjs/react-chartjs-2/blob/master/example/src/charts/Line.js
const data = {
labels: ['1', '2', '3', '4', '5', '6'],
datasets: [
{
label: 'MarketPrice',
data: marketPriceData,
fill: false,
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgba(255, 99, 132, 0.2)',
},
],
};
// useEffect(() => {
// let i = 1;
// if (i < detailPageData.order_history.length) {
// setMarketPriceData(detailPageData.order_history[i].price);
// }
// },[]);
const [detailPageData, setDetailPageData] = useState([]);
useEffect(() => {
fetch(`/data/detailPageData.json`)
.then(res => res.json())
.then(data => setDetailPageData(data.result));
}, []);
useEffect(() => {
fetch(`/data/detailPageData.json`)
.then(res => res.json())
.then(data => setMarketPriceData(data.result.order_history));
}, []);
console.log(marketPriceData);
console.log(marketPriceData[0].price);
// console.log(detailPageData.market_price[0].sizes[0].size);
// console.log(detailPageData.order_history);
// console.log(detailPageData.order_history.length);
// console.log(detailPageData.order_history[0].price);
// console.log(detailPageData.order_history.price);
const [currentMenuIndex, setCurrentMenuIndex] = useState(1);
const handleSelectedMenu = tabIndex => {
setCurrentMenuIndex(tabIndex);
};
const openModal = () => {
setShowModal(prev => !prev);
};
return (
<MarketPriceWrapper>
<TitleWrapper>
<MarketPriceTitle>시세</MarketPriceTitle>
<ShowAllSizes>
{/* <AllSize>230</AllSize>
<FaRegArrowAltCircleDown /> */}
<DropDownMenu className={DropDownMenu} />
</ShowAllSizes>
</TitleWrapper>
<SalesGraphWrapper>
<Line data={data} options={options} />
</SalesGraphWrapper>
<ButtonsWrapper>
<Button
isActive={currentMenuIndex === 1}
onClick={() => handleSelectedMenu(1)}
>
체결 거래
</Button>
<Button
isActive={currentMenuIndex === 2}
onClick={() => handleSelectedMenu(2)}
>
판매 입찰
</Button>
<Button
isActive={currentMenuIndex === 3}
onClick={() => handleSelectedMenu(3)}
>
구매 입찰
</Button>
</ButtonsWrapper>
<TableWrapper>
<TableColumnSetting>
<SizeName>사이즈</SizeName>
<PriceName>거래가</PriceName>
<DateName>거래일</DateName>
</TableColumnSetting>
<RowSection>
{/* {detailPageData.market_price &&
detailPageData.market_price.map(price => {
return console.log(price.sizes[0]);
<Size key={price.id}>
<Size>{price.sizes[price.id].size}</Size>
<Price>{price.sizes[price.id].avg_price}</Price>
<Date>{price.date}</Date>
</Size>
})} */}
<Size />
<Price>24,000원</Price>
<Date>99/11/17</Date>
</RowSection>
<RowSection>
<Size />
<Price>24,000원</Price>
<Date>99/11/17</Date>
</RowSection>
<RowSection>
<Size />
<Price>24,000원</Price>
<Date>99/11/17</Date>
</RowSection>
<RowSection>
<Size />
<Price>24,000원</Price>
<Date>99/11/17</Date>
</RowSection>
<RowSection>
<Size />
<Price>24,000원</Price>
<Date>99/11/17</Date>
</RowSection>
<OrderHistoryButton onClick={openModal}>
체결 내역 더보기
</OrderHistoryButton>
<Modal showModal={showModal} setShowModal={setShowModal}>
<TransitionHistoryModalStyling
detailPageDataMarket={detailPageData}
/>
</Modal>
</TableWrapper>
</MarketPriceWrapper>
);
}
const MarketPriceWrapper = styled.div`
width: 550px;
margin-top: 40px;
padding-left: 40px;
`;
const TitleWrapper = styled.div`
display: flex;
padding-top: 19px;
padding-bottom: 12px;
border-bottom: 1px solid #ebebeb;
`;
const SalesGraphWrapper = styled.div``;
const MarketPriceTitle = styled.span`
padding-top: 4px;
display: inline-block;
line-height: 16px;
font-weight: bold;
color: #222;
`;
const ShowAllSizes = styled.div`
margin-left: 370px;
`;
const AllSize = styled.span`
display: inline-block;
margin-right: 5px;
margin-left: 350px;
font-size: 18px;
`;
const ButtonsWrapper = styled.div`
display: flex;
justify-content: space-evenly;
margin-top: 40px;
border-radius: 10px;
background-color: #f4f4f4;
`;
const Button = styled.button`
display: block;
margin: 2px;
line-height: 16px;
padding: 7px 0 9px;
width: 400px;
font-size: 13px;
text-align: center;
border-radius: 8px;
border: none;
background-color: ${props => (props.isActive ? '#ffff' : '#f4f4f4')};
color: rgba(34, 34, 34, 0.8);
`;
const TableWrapper = styled.div`
margin-top: 40px;
`;
const TableColumnSetting = styled.div`
border-bottom: 1px solid #ebebeb;
padding-bottom: 9px;
text-align: right;
`;
const SizeName = styled.span`
display: inline-block;
margin-right: 250px;
font-size: 12px;
color: rgba(34, 34, 34, 0.5);
font-weight: 400;
`;
const PriceName = styled.span`
display: inline-block;
margin-right: 150px;
font-size: 12px;
color: rgba(34, 34, 34, 0.5);
font-weight: 400;
`;
const DateName = styled.span`
display: inline-block;
font-size: 12px;
color: rgba(34, 34, 34, 0.5);
font-weight: 400;
`;
const RowSection = styled.div`
margin-top: 4px;
text-align: right;
`;
const Size = styled.span`
display: inline-block;
margin-right: 240px;
color: #222;
font-size: 12px;
`;
const Price = styled.span`
display: inline-block;
margin-right: 130px;
color: #222;
font-size: 12px;
`;
const Date = styled.span`
display: inline-block;
color: #222;
font-size: 12px;
`;
const OrderHistoryButton = styled.button`
border: 1px solid #d3d3d3;
margin-top: 40px;
background-color: #ffffff;
color: rgba(34, 34, 34, 0.8);
font-weight: 400;
padding: 0 18px;
width: 500px;
height: 42px;
border-radius: 12px;
font-size: 14px;
`;
const const [marketPriceData, setMarketPriceData] = useState([]);
useEffect(() => {
fetch(`/data/detailPageData.json`)
.then(res => res.json())
.then(data => setMarketPriceData(data.result.order_history.map(item => item.price));
}, []);
maybe you can use map in your object data result.
data.result.order_history.map((item)=>{
return item.price
}
That will only get de price property in in your array.
setMarketPriceData(data.result.order_history.map((item)=>{
return item.price
})

Type 'JSX.Element' is not assignable to type 'Element'

Previously had this build 'building' but have upgraded my dependencies and now having many more build errors than before. Have been running through them but this one I can't seem to resolve. I have tried multiple implementations of:
children: Element[];
Element: JSX.Element;
I have removed the JSX.Element from this line: const VideoSlider: React.FC<Props> = ({ content }) => { in hopes of fixing this but I'm not sure..
I don't really know Typescript so any help would be greatly appreciated as I would like to get this build built again..
Error:
╰─ yarn run build
yarn run v1.22.10
$ next build
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
warn - Minimum recommended TypeScript version is v4.3.2, older versions can potentially be incompatible with Next.js. Detected: 3.9.10
Failed to compile.
./components/VideoSlider/index.tsx:194:9
Type error: No overload matches this call.
Overload 2 of 2, '(props: StyledComponentPropsWithAs<"div", any, { selectSVG: any; selectBackgroundImage: any; selectVerticalOrientation: any; backgroundSVG: unknown; backgroundImage: unknown; children: Element[]; Element: Element; }, never, "div", "div">): ReactElement<...>', gave the following error.
Type 'JSX.Element' is not assignable to type 'Element'.
Overload 2 of 2, '(props: StyledComponentPropsWithAs<"div", any, { selectSVG: any; selectBackgroundImage: any; selectVerticalOrientation: any; backgroundSVG: unknown; backgroundImage: unknown; children: Element[]; Element: Element; }, never, "div", "div">): ReactElement<...>', gave the following error.
Type 'JSX.Element' is not assignable to type 'Element'.
192 | selectVerticalOrientation={content.selectVerticalOrientation}
193 | >
> 194 | <FirstSlider>
| ^
195 | <div className="first-slider background-image-slider">
196 | <Slider
197 | asNavFor={nav2}
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Component/.tsx file:
/* eslint-disable react/jsx-props-no-spreading */
import React, { useState, useRef, useEffect } from "react";
import Slider from "react-slick";
import { Props } from "./interface";
import Video from "../Video";
import {
VideoSliderComp,
SliderArrows,
FirstSlider,
VideoCopy,
Slider2Container,
Slider3Container,
SliderButtonComp,
MissingImage,
ImageContainer,
} from "./styled";
import Layout from "../Layout";
import TextBlock from "../TextBlock";
import { serializers } from "../serializer";
import client from "../../lib/sanityClient";
import imageUrlBuilder from "#sanity/image-url";
import { SanityImageSource } from "#sanity/image-url/lib/types/types";
import MiniTitleComp from "../MiniTitle";
const VideoSlider: React.FC<Props> = ({ content }) => {
const [nav1, setNav1] = useState(null);
const [nav2, setNav2] = useState(null);
const [nav3, setNav3] = useState(null);
const slider1 = useRef(null);
const slider2 = useRef(null);
const slider3 = useRef(null);
useEffect(() => {
setNav1(slider1.current);
setNav2(slider2.current);
setNav3(slider3.current);
}, []);
const gotoNext = () => {
slider1.current.slickNext();
};
const gotoPrev = () => {
slider1.current.slickPrev();
};
const Button = (props: {
func?: any;
text?: string;
first?: boolean;
last?: boolean;
right?: boolean;
}) => {
const { func, text, first, last } = props;
return (
<SliderButtonComp
first={first}
last={last}
role="button"
className="arrow-btn prev"
onClick={func}
>
<div className="button-container">
<span className="button">{text}</span>
</div>
</SliderButtonComp>
);
};
const renderArrows = (i: string | any[]) => {
if (i.length > 1) {
return (
<SliderArrows className="slider-arrow">
<Button first text="Previous" func={gotoPrev} />
<Button last text="Next" func={gotoNext} right />
</SliderArrows>
);
}
};
const renderInfo = (slide: {
videoTitle:
| boolean
| React.ReactChild
| React.ReactFragment
| React.ReactPortal;
videoDescription: any;
}) => {
return (
<>
<h2>{slide.videoTitle}</h2>
<TextBlock
props={slide.videoDescription}
blocks={slide.videoDescription}
serializers={serializers}
imageOptions={{
width: 500,
height: 500,
}}
/>
</>
);
};
const settings = {
customPaging: function () {
return <div className="dot"></div>;
},
dotsClass: "slick-dots slick-thumb",
};
const checkSlideCountRenderDots = (count: string | any[]) => {
if (count.length > 1) {
return true;
} else {
return false;
}
};
function SvgMissingImage(
props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>
) {
return (
<MissingImage>
<svg
xmlns="http://www.w3.org/2000/svg"
data-name="Layer 1"
viewBox="0 0 100 100"
{...props}
>
<path d="M57.21 27.558h22.997L57.21 8.032v19.526z" />
<path d="M59.813 73.568H7.359V9.138h42.104v21.356a4.812 4.812 0 004.81 4.811H78.36v22.24a21.103 21.103 0 011.847-.086 21.036 21.036 0 013.007.22v-23.5a3.734 3.734 0 00-3.734-3.733H56.487a2.165 2.165 0 01-2.166-2.165V8.013a3.734 3.734 0 00-3.733-3.734H4.559A2.06 2.06 0 002.5 6.34v70.03a2.06 2.06 0 002.06 2.058h54.678a20.933 20.933 0 01.575-4.859z" />
<circle cx={41.062} cy={36.333} r={4.276} />
<path d="M66.474 62.602L59.797 50.04a1.234 1.234 0 00-1.777-.446l-8.762 5.875a1.234 1.234 0 01-1.865-.656l-2.797-8.897a1.234 1.234 0 00-1.649-.77l-7.163 2.96a1.234 1.234 0 01-1.484-.435l-4.694-6.736a1.234 1.234 0 00-2.14.203l-11.777 26.4a1.234 1.234 0 001.128 1.737h44.528a21.086 21.086 0 015.129-6.673zM80.207 61.134A17.293 17.293 0 1097.5 78.427a17.293 17.293 0 00-17.293-17.293zm-2.919 7.833a2.924 2.924 0 013.204-2.908 3.005 3.005 0 012.633 3.03v10.354a2.919 2.919 0 01-5.837 0zm2.919 21.842a3.007 3.007 0 113.007-3.007 3.007 3.007 0 01-3.007 3.007z" />
</svg>
</MissingImage>
);
}
const imageOrVideo = (props: { videoSliderBuilder: any }) => {
return content.videoSliderBuilder.map(
(
slide: {
selectContentType: string;
image: any;
videoUrl: string;
selectVideoType: string;
video: any;
video2: any;
},
index: string
) => {
if (slide.selectContentType === "images") {
let url = urlFor(slide.image).url();
if (url === null) {
return <SvgMissingImage />;
} else {
return <img key={"imgfromVideoSliderBuilder" + index} src={url} />;
}
} else if (slide.selectContentType === "video") {
return (
<div key={"video-slide" + index}>
<Video
type="slider"
key={"video" + index}
videoUrl={slide.videoUrl}
selectVideoType={slide.selectVideoType}
videoSliderFile={[slide.video, slide.video2]}
playing={slide.selectVideoType}
/>
</div>
);
}
}
);
};
// Get a pre-configured url-builder from your sanity client
const builder = imageUrlBuilder(client);
// Then we like to make a simple function like this that gives the
// builder an image and returns the builder for you to specify additional
// parameters:
function urlFor(source: SanityImageSource) {
return builder.image(source);
}
return (
<>
<VideoSliderComp
key={content.videoSlider2Title}
selectVerticalOrientation={content.selectVerticalOrientation}
>
<FirstSlider>
<div className="first-slider background-image-slider">
<Slider
asNavFor={nav2}
ref={slider1}
slidesToShow={1}
fade={true}
arrows={false}
speed={1000}
focusOnSelect={false}
accessibility={false}
>
{content.videoSliderBuilder.map(
(slide: {
backgroundSVG: any;
backgroundImage: any;
selectSVG: boolean;
selectBackgroundImage: React.Key;
}) => {
console.log("slide:", slide);
const svgUrl = urlFor(slide.backgroundSVG).url();
const backgroundImageUrl = urlFor(
slide.backgroundImage
).url();
if (
slide.selectSVG === true &&
slide.backgroundSVG !== undefined &&
backgroundImageUrl !== undefined
) {
return (
<>
<div className="crest-container">
<img src={svgUrl ? svgUrl : null} />
</div>
<ImageContainer
className="imageContainer"
selectBackgroundImage={slide.selectBackgroundImage}
>
<img
src={backgroundImageUrl ? backgroundImageUrl : null}
/>
</ImageContainer>
</>
);
} else if (
slide.selectSVG === false &&
backgroundImageUrl !== undefined
) {
return (
<ImageContainer
key={slide.selectBackgroundImage}
selectBackgroundImage={slide.selectBackgroundImage}
>
<img
src={backgroundImageUrl ? backgroundImageUrl : null}
/>
</ImageContainer>
);
} else {
return null;
}
}
)}
</Slider>
</div>
<Layout>
<div className="content-container">
<Slider2Container
selectVerticalOrientation={content.selectVerticalOrientation}
selectOrientation={content.videoSliderBuilder}
className="second-slider video-content-slider"
>
<MiniTitleComp title={content.videoSlider2Title} />
<Slider
asNavFor={nav3}
ref={slider2}
slidesToShow={1}
swipeToSlide={true}
focusOnSelect={false}
arrows={false}
dots={checkSlideCountRenderDots(content.videoSliderBuilder)}
{...settings}
>
{imageOrVideo(content)}
</Slider>
{renderArrows(content.videoSliderBuilder)}
</Slider2Container>
<Slider3Container
selectOrientation={content.selectOrientation}
className="third-slider further-info-slider"
selectVerticalOrientation={content.selectVerticalOrientation}
>
<Slider
asNavFor={nav1}
ref={slider3}
slidesToShow={1}
swipeToSlide={true}
focusOnSelect={true}
arrows={false}
dots={false}
fade={true}
speed={1000}
accessibility={false}
>
{content.videoSliderBuilder.map(
(slide: any, index: string) => {
return (
<div
key={"videoSlideBuilder" + index}
className="further-info-slider__slide"
>
<VideoCopy>{renderInfo(slide)}</VideoCopy>
</div>
);
}
)}
</Slider>
</Slider3Container>
</div>
</Layout>
</FirstSlider>
</VideoSliderComp>
</>
);
};
export default VideoSlider;
styled.tsx / styled-components:
import styled from "styled-components";
const renderOverlay = (prop: boolean) => {
let css: string;
if (prop === true) {
css = "";
} else if (prop === false) {
css = null;
}
return css;
};
const renderVerticalOrientation = (prop: string) => {
let css: string;
if (prop === "top") {
css = "flex-start";
} else if (prop === "center") {
css = "center";
} else if (prop === "bottom") {
css = "flex-end";
}
return css;
};
export const MiniTitle = styled.p`
margin-bottom: 20px;
text-align: left;
font-size: 14px;
background: #333333;
color: #cecece;
padding: 4px 8px;
display: inline-flex;
align-self: flex-start;
`;
export const ImageContainer = styled.div<{ selectBackgroundImage: any }>`
position: relative;
&::after {
content: "";
background: #151515;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: ${(props: { selectBackgroundImage: any }) =>
props.selectBackgroundImage ? "0.666" : null};
}
`;
export const FirstSlider = styled.div<{
selectSVG: any;
selectBackgroundImage: any;
selectVerticalOrientation: any;
backgroundSVG: unknown;
backgroundImage: unknown;
children: Element[];
Element: JSX.Element;
}>`
height: 100vh;
width: 100%;
object-fit: contain;
position: relative;
margin-bottom: 100px;
.crest-container {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
img {
object-fit: cover;
height: 100%;
opacity: 0.855;
transform: scale(1);
}
}
.first-slider {
height: 100%;
.slick-slider,
.slick-list {
height: 100%;
}
img {
width: 100%;
}
position: relative;
&::after {
content: ${(props: { selectSVG: any }) => (props.selectSVG ? "" : null)};
content: ${(props: { selectBackgroundImage: any }) =>
props.selectBackgroundImage ? "" : null};
background: #151515;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 1.888;
}
.slick-slide {
position: relative;
&:after {
content: ${(props: { selectSVG: any }) =>
props.selectSVG ? renderOverlay(props.selectSVG) : null};
content: ${(props: { selectBackgroundImage: any }) =>
props.selectBackgroundImage
? renderOverlay(props.selectBackgroundImage)
: null};
background: #151515;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 1.888;
}
}
}
#media only screen and (max-width: 1000px) {
height: auto;
overflow: hidden;
.first-slider {
.slick-slide {
height: 100vh;
height: 100%;
div,
img {
height: 100%;
}
}
}
}
`;
export const SliderContainer = styled.div`
.second-slider,
.third-slider {
display: flex;
flex-direction: column;
justify-content: center;
}
`;
export const MissingImage = styled.div`
display: flex;
justify-content: center;
align-items: center;
svg {
width: 33.333% !important;
fill: #e62117;
}
`;
export const Slider2Container = styled.div<{
selectVerticalOrientation: any;
selectOrientation: any;
}>`
display: flex;
flex-direction: column;
justify-content: center;
justify-content: ${(props: { selectVerticalOrientation: any }) =>
props.selectVerticalOrientation
? renderVerticalOrientation(props.selectVerticalOrientation)
: null};
grid-area: content;
grid-area: ${(props: { selectOrientation: any }) =>
props.selectOrientation ? "additional" : "content"};
`;
export const Slider3Container = styled(SliderContainer)<{
selectOrientation: any;
selectVerticalOrientation: any;
}>`
display: flex;
flex-direction: column;
grid-area: additional;
grid-area: ${(props: { selectOrientation: any }) =>
props.selectOrientation ? "additional" : "content"};
justify-content: ${(props: { selectVerticalOrientation: any }) =>
props.selectVerticalOrientation
? renderVerticalOrientation(props.selectVerticalOrientation)
: null};
.slick-slider {
width: 100%;
}
`;
export const VideoSliderComp = styled.div<{
selectVerticalOrientation?: any;
selectBackgroundImage?: any;
}>`
div {
&:focus {
outline: none !important;
}
}
.slick-dots {
position: absolute;
bottom: -40px;
width: 100%;
width: auto;
left: 0;
li {
height: initial;
width: initial;
}
z-index: 3;
.dot {
background: transparent;
background: #969696;
border: 1px solid var(--nice-dark);
border: none;
padding: 3px;
border-radius: 100%;
transform: scale(0.9333);
}
.slick-active .dot {
background: white;
transform: scale(1);
}
}
.layout {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
left: 50%;
transform: translate(-50%, 0);
}
.content-container {
height: 100%;
width: 100%;
display: grid;
grid-template-columns: 50% 50%;
grid-template-rows: auto;
grid-template-areas:
"content additional"
"content additional";
}
.second-slider {
grid-area: content;
height: 100%;
width: 100%;
z-index: 2;
.slick-list {
overflow: hidden;
}
.slick-initialized .slick-slide {
display: flex;
justify-content: center;
align-items: center;
width: 60%;
overflow: hidden;
opacity: 0;
transition: opacity 1s ease;
transition-delay: 0.2s;
div {
width: 100% !important;
}
}
.slick-slide.slick-active {
opacity: 1;
transition: opacity 1s ease;
transition-delay: 0.1s;
}
iframe,
video {
filter: drop-shadow(0px 4px 0px #333333);
transition: all 0.666s ease-in 0.1s;
&:hover {
filter: drop-shadow(0 4px 0 #484848);
}
}
video {
height: initial;
}
}
.third-slider {
grid-area: additional;
color: white;
align-items: center;
z-index: 4;
}
.further-info-slider__slide {
height: auto;
display: flex !important;
align-items: center;
width: 100%;
overflow-y: scroll;
h2 {
display: inline-block;
position: relative;
}
}
.player-wrapper {
position: relative;
padding-top: 56.25%; /* 720 / 1280 = 0.5625 */
}
.react-player {
position: absolute;
top: 0;
left: 0;
height: 100% !important;
}
#media only screen and (max-width: 1000px) {
.background-image-slider {
position: absolute;
top: 0;
left: 0;
}
.layout {
position: relative;
}
.further-info-slider__slide {
height: auto;
width: 100%;
}
.content-container {
display: flex;
flex-direction: column;
}
.second-slider {
overflow: initial;
margin-bottom: 50px;
}
}
#media only screen and (max-width: 1000px) {
.further-info-slider__slide {
align-items: flex-start;
}
}
`;
export const VideoCopy = styled.div`
padding: 1em;
padding-left: 3em;
padding-right: 3rem;
width: 95%;
margin: 0 auto;
#media only screen and (max-width: 1000px) {
padding: 0;
width: 100%;
}
`;
export const SliderButtonComp = styled.div<{ first: any; last: any }>`
position: relative;
svg {
opacity: 1;
transition: all 0.333s ease-in 0.1s;
}
.button-container {
display: flex;
flex-direction: row;
align-items: center;
}
span {
padding-left: ${(props: { first: any }) => (props.first ? "0" : "3px")};
padding-right: ${(props: { last: any }) => (props.last ? "0" : "3px")};
}
&:hover {
.button-container {
span {
opacity: 1;
}
svg {
opacity: 1;
}
}
}
`;
export const SliderArrows = styled.div`
grid-area: content;
position: absolute;
bottom: -60px;
width: 100%;
position: relative;
display: flex;
height: auto;
color: white;
color: var(--nice-dark);
flex-direction: row;
justify-content: flex-start;
align-items: center;
z-index: 2;
padding: 0;
.arrow-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: all 1s ease;
img {
width: 30px;
}
span {
opacity: 0.8;
transition: all 0.333s ease-in-out;
}
&:hover {
span {
color: #f9f9f9;
}
}
}
.prev {
img {
transform: rotate(-90deg);
}
}
.next {
img {
transform: rotate(-270deg);
}
}
#media only screen and (max-width: 1000px) {
bottom: 0;
}
`;
From what I can understand from your question, you are trying to assign the type functional component (JSX.Element) to an array (Element[]).
Try:
children: Element[];
Element: Array;

React Carousel target div elements

I am learning to make custom Carousel by using React and Typescript. For styling I used styled components and scss. I found from one Article how to make Carousel. My carousel works fine.
I have made four div elements. when the carousel image slide will change, I want to change background-color of the div elements inot orange color. But Don't know how to do that.
I share my code in codesandbox
This is my Carousel component
import React, { useState, useEffect, useRef, memo, useCallback } from "react";
import styled from "styled-components";
interface ICarousel {
children: JSX.Element[];
currentSlide?: number;
autoPlay?: boolean;
dots?: boolean;
interval?: number;
arrow?: boolean;
}
const IMG_WIDTH = 320;
const IMG_HEIGHT = 700;
export default memo(
({
children,
autoPlay = false,
dots = false,
interval = 3000,
arrow = false
}: ICarousel) => {
const [currentSlide, setSlide] = useState(0);
const [isPlaying, setIsPlaying] = useState(autoPlay);
const timer = useRef<any>(undefined);
const slides = children.map((slide, index) => (
<CarouselSlide key={index}>{slide}</CarouselSlide>
));
const handleSlideChange = useCallback(
(index: number) =>
setSlide(
index > slides.length - 1 ? 0 : index < 0 ? slides.length - 1 : index
),
[slides]
);
const createInterval = useCallback(() => {
timer.current = setInterval(() => {
handleSlideChange(currentSlide + 1);
}, interval);
}, [interval, handleSlideChange, currentSlide]);
const destroyInterval = useCallback(() => {
clearInterval(timer.current);
}, []);
useEffect(() => {
if (autoPlay) {
createInterval();
return () => destroyInterval();
}
}, [autoPlay, createInterval, destroyInterval]);
return (
<CarouselContainer
onMouseEnter={() => {
if (autoPlay) {
destroyInterval();
}
}}
onMouseLeave={() => {
if (autoPlay) {
createInterval();
}
}}
>
<CarouselSlides currentSlide={currentSlide}>{slides}</CarouselSlides>
{arrow ? (
<div>
<LeftButton onClick={() => handleSlideChange(currentSlide - 1)}>
❮
</LeftButton>
<RightButton onClick={() => handleSlideChange(currentSlide + 1)}>
❯
</RightButton>
</div>
) : null}
{dots ? (
<Dots>
{slides.map((i, index) => (
<Dot
key={index}
onClick={() => handleSlideChange(index)}
active={currentSlide === index}
/>
))}
</Dots>
) : null}
</CarouselContainer>
);
}
);
const Buttons = styled.a`
cursor: pointer;
position: relative;
font-size: 18px;
transition: 0.6s ease;
user-select: none;
height: 50px;
width: 40px;
display: flex;
justify-content: center;
align-items: center;
align-content: center;
top: calc(50% - 25px);
position: absolute;
&:hover {
background-color: rgba(0, 0, 0, 0.8);
}
`;
const RightButton = styled(Buttons)`
border-radius: 3px 0 0 3px;
right: 0;
`;
const LeftButton = styled(Buttons)`
border-radius: 0px 3px 3px 0px;
left: 0;
`;
const Dots = styled.div`
display: flex;
justify-content: center;
align-items: center;
align-content: center;
margin-top: 10px;
`;
const Dot = styled.span<{ active: boolean }>`
cursor: pointer;
height: 15px;
width: 15px;
margin: 0 10px;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
background-color: ${({ active }) => (active ? `red` : `#eeeeee`)};
`;
const CarouselContainer = styled.div`
overflow: hidden;
position: relative;
width: ${IMG_WIDTH}px;
height: ${IMG_HEIGHT}px;
img {
/* change the margin and width to fit the phone mask */
width: ${IMG_WIDTH - 20}px;
height: ${IMG_HEIGHT - 50}px;
margin-left: 10px;
margin-top: 15px;
}
z-index: 1;
`;
const CarouselSlide = styled.div`
flex: 0 0 auto;
transition: all 0.5s ease;
width: 100%;
`;
const CarouselSlides = styled.div<{
currentSlide: ICarousel["currentSlide"];
}>`
display: flex;
${({ currentSlide }) =>
` transform: translateX(-${currentSlide ? currentSlide * 100 + `%` : 0})`};
transition: transform 300ms linear;
cursor: pointer;
`;
This where I am using the Carousel component
import * as React from "react";
import "./styles.scss";
import Carousel from "./carousel";
export const imgUrls = [
`https://images.unsplash.com/photo-1455849318743-b2233052fcff?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60`,
`https://images.unsplash.com/photo-1508138221679-760a23a2285b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60`,
`https://images.unsplash.com/photo-1519125323398-675f0ddb6308?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60`,
`https://images.unsplash.com/photo-1494253109108-2e30c049369b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60`
];
export default function App() {
return (
<div className="App">
<main className="Testeru">
<div className="phone-left">
// I want change background color of this div element
<div className="phone-left-upper">left upper</div>
<div className="phone-left-lower">left-lower</div>
</div>
<div className="phone-slider">
<div className="mobile_overlay">
<Carousel autoPlay>
{imgUrls.map((i) => {
return (
<img
key={i}
src={i}
alt=""
style={{
borderRadius: `20px`
}}
/>
);
})}
</Carousel>
</div>
</div>
// I want change background color of this div element
<div className="phone-right">
<div className="phone-right-upper">right upper</div>
<div className="phone-right-lower">right-lower</div>
</div>
</main>
</div>
);
}

Ref issue using react-hook-form

I'm trying to create a form validation with react-hook-form in my current project. I've already tried different approaches but always I got errors because of the ref attribute. If I change the <FormField> to input, it starts to work.
Any idea how to solve this?
Contact
import React from 'react';
import { useForm } from "react-hook-form";
import FormField from '../../components/FormField';
import Button from '../../components/Button';
const Contact = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = values => console.log(values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
name="email"
onChange={() => { console.log("changed!") }}
ref={register({
required: "Required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "invalid email address"
}
})}
/>
<p style={{ color: "red" }}>
{errors.email && errors.email.message}
</p>
<Button>Submit</Button>
</form>
);
};
export default Contact;
FormField
import React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
const FormFieldWrapper = styled.div`
position: relative;
textarea {
min-height: 150px;
}
input[type="color"] {
padding-left: 67px;
}
`;
const Label = styled.label``;
Label.Text = styled.span`
color: #e5e5e5;
height: 57px;
position: absolute;
top: 0;
left: 16px;
display: flex;
align-items: center;
transform-origin: 0% 0%;
font-size: 18px;
font-style: normal;
font-weight: 300;
transition: 0.1s ease-in-out;
`;
const Input = styled.input`
background: #53585d;
color: #f5f5f5;
display: block;
width: 100%;
height: 57px;
font-size: 18px;
outline: 0;
border: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #53585d;
padding: 16px 16px;
margin-bottom: 45px;
resize: none;
border-radius: 4px;
transition: border-color 0.3s;
&:focus {
border-bottom-color: var(--primary);
}
&:focus:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
${({ value }) => {
const hasValue = value.length > 0;
return (
hasValue &&
css`
&:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
`
);
}}
`;
function FormField({ label, type, name, value, onChange, ref }) {
const isTypeTextArea = type === "textarea";
const tag = isTypeTextArea ? "textarea" : "input";
return (
<FormFieldWrapper>
<Label>
<Input
as={tag}
type={type}
value={value}
name={name}
onChange={onChange}
ref={ref}
/>
<Label.Text>{label}:</Label.Text>
</Label>
</FormFieldWrapper>
);
}
FormField.defaultProps = {
type: "text",
value: "",
};
FormField.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
type: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ref: PropTypes.func
};
export default FormField;
Errors:
Referring to the docs the register should be used as below so we won't get refs issues and also the register will change the value inside the input so we don't need to pass a value prop :
Contact :
import React from "react";
import { useForm } from "react-hook-form";
import FormField from "../../components/FormField";
import Button from "../../components/Button";
const Contact = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = (values) => console.log("values", values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
name="email"
onChange={() => {
console.log("changed!");
}}
register={register}
/>
<p style={{ color: "red" }}>{errors.email && errors.email.message}</p>
<Button>Submit</Button>
</form>
);
};
export default Contact;
FormField :
import React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
const FormFieldWrapper = styled.div`
position: relative;
textarea {
min-height: 150px;
}
input[type="color"] {
padding-left: 67px;
}
`;
const Label = styled.label``;
Label.Text = styled.span`
color: #e5e5e5;
height: 57px;
position: absolute;
top: 0;
left: 16px;
display: flex;
align-items: center;
transform-origin: 0% 0%;
font-size: 18px;
font-style: normal;
font-weight: 300;
transition: 0.1s ease-in-out;
`;
const Input = styled.input`
background: #53585d;
color: #f5f5f5;
display: block;
width: 100%;
height: 57px;
font-size: 18px;
outline: 0;
border: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #53585d;
padding: 16px 16px;
margin-bottom: 45px;
resize: none;
border-radius: 4px;
transition: border-color 0.3s;
&:focus {
border-bottom-color: var(--primary);
}
&:focus:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
${({ value = {} }) => { // here you should find an other approch because there is no value props
const hasValue = value.length > 0;
return (
hasValue &&
css`
&:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
`
);
}}
`;
const FormField = ({ label, type, name, onChange, register }) => {
const isTypeTextArea = type === "textarea";
const tag = isTypeTextArea ? "textarea" : "input";
return (
<FormFieldWrapper>
<Label>
<Input
as={tag}
type={type}
// value={value} it's not a controlled input! so the register'ill provide the value
name={name}
onChange={onChange}
ref={register({
required: "Required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "invalid email address",
},
})}
/>
<Label.Text>{label}:</Label.Text>
</Label>
</FormFieldWrapper>
);
};
FormField.defaultProps = {
type: "text",
value: "",
};
FormField.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
type: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ref: PropTypes.func,
};
export default FormField;

How would this React code look without React-hooks and with class components rather than functional components?

I just studied React from YouTube lessons, and there all the lessons were built on classes and the usual this.setState, without hooks. How would this React code look without React-hooks and with class components rather than functional components?
The code itself implements an image slider:
React:
function Slider({ items }) {
const [ active, setActive ] = React.useState(0);
const { length, [active]: slide } = items;
const next = e => setActive((active + +e.target.dataset.step + length) % length);
const goTo = e => setActive(+e.target.dataset.index);
React.useEffect(() => {
const timeout = setTimeout(() => setActive((active + 1 + length) % length), 5000);
return () => clearTimeout(timeout);
}, [active, length]);
return (
<div>
<div className="slideshow-container">
<div className="mySlides fade">
<div className="numbertext">{active + 1} / {length}</div>
<img src={slide.img} />
<div className="text">{slide.title}</div>
</div>
<a className="prev" onClick={next} data-step={-1}>❮</a>
<a className="next" onClick={next} data-step={+1}>❯</a>
</div>
<div className="dots">
{items.map((n, i) => (
<span
key={n.id}
className={`dot ${i === active ? 'active' : ''}`}
onClick={goTo}
data-index={i}
></span>
))}
</div>
</div>
);
}
const items = [
{ title: 'One', img: 'https://upload.wikimedia.org/wikipedia/commons/1/1f/Purity_of_nature.jpg' },
{ title: 'Two', img: 'https://mairie-balma.fr/wp-content/uploads/2016/06/Lhers.jpg' },
{ title: 'Three', img: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRt-b1iBqHQ_emkm1wFmkM7KQskzIqg7YQPZWW85Sa7k2nNLwgjMw' },
].map((n, i) => ({ ...n, id: i + 1 }));
ReactDOM.render(<Slider items={items} />, document.getElementById('app'));
HTML
<div id="app"></div>
CSS:
.slideshow-container {
max-width: 500px;
position: relative;
margin: auto;
}
.prev, .next {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
padding: 16px;
margin-top: -22px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
}
.next {
right: 0;
border-radius: 3px 0 0 3px;
}
.prev:hover, .next:hover {
background-color: rgba(0,0,0,0.8);
}
.text {
color: #f2f2f2;
font-size: 15px;
padding: 8px 12px;
position: absolute;
bottom: 8px;
width: 100%;
text-align: center;
box-sizing: border-box;
}
.numbertext {
color: #f2f2f2;
font-size: 12px;
padding: 8px 12px;
position: absolute;
top: 0;
}
.dots {
display: flex;
justify-content: center;
}
.dot {
cursor: pointer;
height: 15px;
width: 15px;
margin: 0 2px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
}
.active, .dot:hover {
background-color: #717171;
}
.mySlides img {
width: 100%;
}
Something like this (not fully tested):
class Slider {
constructor(props) {
super(props);
this.state = {
active: 0
}
}
let timeout = null;
componentDidMount() {
this.timeout = setTimeout(() => this.setActive(), 5000);
}
componentDidUpdate(prevProps) {
const { active } = this.props;
if (prevProps.active !=== active {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => this.setActive(), 5000);
});
}
componentDidUnmount() {
if (this.timeout) {
clearTimeout(this.timeout);
}
}
const setActive = (newActive) => {
const { length } = items;
this.setState({
active: (newActive + 1 + length) % length
});
}
const next = e => {
const { length } = items;
this.setActive((this.state.active + +e.target.dataset.step + length) % length);
}
const goTo = e => this.setActive(+e.target.dataset.index);
render() {
const { length } = items;
const {active} = this.state;
return (
<div>
<div className="slideshow-container">
<div className="mySlides fade">
<div className="numbertext">{active + 1} / {length}</div>
<img src={slide.img} />
<div className="text">{slide.title}</div>
</div>
<a className="prev" onClick={this.next} data-step={-1}>❮</a>
<a className="next" onClick={this.next} data-step={+1}>❯</a>
</div>
<div className="dots">
{items.map((n, i) => (
<span
key={n.id}
className={`dot ${i === active ? 'active' : ''}`}
onClick={this.goTo}
data-index={i}
></span>
))}
</div>
</div>
);
}
}
const items = [
{ title: 'One', img: 'https://upload.wikimedia.org/wikipedia/commons/1/1f/Purity_of_nature.jpg' },
{ title: 'Two', img: 'https://mairie-balma.fr/wp-content/uploads/2016/06/Lhers.jpg' },
{ title: 'Three', img: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRt-b1iBqHQ_emkm1wFmkM7KQskzIqg7YQPZWW85Sa7k2nNLwgjMw' },
].map((n, i) => ({ ...n, id: i + 1 }));
ReactDOM.render(<Slider items={items} />, document.getElementById('app'));

Resources