Dont working prev and next methods at flicking slider - reactjs

i have two buttons, but thay are not move slider. maybee anyone have an example?
<button className="carousel__back-button" onClick={() => flicking.current.prev()}> </button>
<button className="carousel__next-button" onClick={() => flicking.current.next()}> </button>
----------initial flicking options----------------------------------------------------------------------
let flicking: React.RefObject<Flicking> =
React.createRef<Flicking>()
let oprionsFlicking = {
className: "flicking",
ref: flicking,
hanger: "0",
anchor: "0",
bound: true,
gap: 5
}
-----------use here-----------------------------------------------------------------------------------------
<StyledDateWrapper>
<Flicking ref={flicking} {...oprionsFlicking} style={{height: '51px', width: '100%'}}>
{ currentMonthDates.length > 0 && Object.entries(currentMonthDates
.reduce((a, i) => {
const normolizeDate = i * 1000;
const dayNumber = new Date(normolizeDate).getDate()
const dayWeek = new Date(normolizeDate).getDay()
return {...a, [dayNumber]: { dayWeek, checked: false }}
}, {}))
.map((el: any) => el[0] == checkedDay ? ([...el, el[1].checked = true]) : el)
.map(([date, day]: any) => {
const monthIdx = new Date(currentMonth.start ? currentMonth.start * 1000 : new Date()).getMonth();
const month = monthesMapper[monthIdx]
return <StyledDateTabWrapper key={date} className="panel">
<DateTab
onClick={() => takeDay(date)}
selected={day.checked}
date={`${date} ${month}`}
day={`${daysInWeek[day.dayWeek - 1] ? daysInWeek[day.dayWeek - 1] : 'Вс'}`}
holiday={daysInWeek[day.dayWeek - 1] ? false : true}
theme={PrimaryThemeConfig}/>
</StyledDateTabWrapper>
}) }
</Flicking>
{ renderArrows() }
</StyledDateWrapper>
p.s. react functional component

Related

React Guarantee that part of function runs after DOM updates

Currently I have a textarea like this:
<textarea
onChange={handleTextAreaChange}
ref={textAreaRef as MutableRefObject<HTMLTextAreaElement>}
id={id}
value={content}
></textarea>
I am implementing some buttons to add markdown to the textarea to make it easier for the user to update and have this function for bold:
const handleBoldClick = useCallback(() => {
const selectionStart = textAreaRef.current?.selectionStart;
const selectionEnd = textAreaRef.current?.selectionEnd;
if (selectionStart && selectionEnd) {
setContent(
prevContent =>
prevContent.substring(0, selectionStart) +
'**' +
prevContent.substring(selectionStart, selectionEnd) +
'**' +
prevContent.substring(selectionEnd, prevContent.length)
);
} else {
setContent(prevContent => prevContent + '****');
// Want this to run after textarea gets updated
textAreaRef.current?.focus();
textAreaRef.current?.setSelectionRange(
content.length - 3,
content.length - 3
);
}
const changeEvent = new Event('change', { bubbles: true });
// Want to run this after textarea is updated
textAreaRef.current?.dispatchEvent(changeEvent);
}, [content]);
setContent is the setter for content which is passed to the textarea. Is there a way to guarantee the parts I've marked with comments as wanting to only run once the DOM gets updated run when I want them to?
I finagled around with things and went with this approach (gonna post the entire component, which contains some stuff irrelevant to the question):
const MarkdownTextArea = ({
value,
onBlur = () => {},
onChange = () => {},
touched = false,
error,
id,
label
}: MarkdownTextAreaProps) => {
const [content, setContent] = useState(value ?? '');
const [numberOfRows, setNumberOfRows] = useState(5);
const [numberOfCols, setNumberOfCols] = useState(20);
const [isPreview, setIsPreview] = useState(false);
const [changed, setChanged] = useState<'bold' | null>();
const textAreaRef = useRef<HTMLTextAreaElement>();
useEffect(() => {
const setColsAndRows = () => {
const newColumnsNumber = Math.floor(
(textAreaRef.current?.offsetWidth ?? 100) /
(convertRemToPixels(1.2) / 1.85)
);
setNumberOfCols(newColumnsNumber);
setNumberOfRows(calculateNumberOfRows(content, newColumnsNumber));
};
setColsAndRows();
window.addEventListener('resize', setColsAndRows);
return () => {
window.removeEventListener('resize', setColsAndRows);
};
}, [content]);
const handleTextAreaChange: ChangeEventHandler<HTMLTextAreaElement> =
useCallback(
event => {
onChange(event);
setContent(event.target.value);
setNumberOfRows(
calculateNumberOfRows(
event.target.value,
textAreaRef.current?.cols ?? 20
)
);
},
[onChange]
);
const handleBoldClick = useCallback(() => {
const selectionStart = textAreaRef.current?.selectionStart;
const selectionEnd = textAreaRef.current?.selectionEnd;
if (selectionStart && selectionEnd) {
setContent(
prevContent =>
prevContent.substring(0, selectionStart) +
'**' +
prevContent.substring(selectionStart, selectionEnd) +
'**' +
prevContent.substring(selectionEnd, prevContent.length)
);
} else {
setContent(prevContent => prevContent + '****');
}
setChanged('bold');
}, []);
if (changed && textAreaRef.current?.value === content) {
const changeEvent = new Event('change', { bubbles: true });
textAreaRef.current?.dispatchEvent(changeEvent);
if (changed === 'bold' && textAreaRef.current) {
textAreaRef.current.focus();
textAreaRef.current.selectionStart = content.length - 2;
textAreaRef.current.selectionEnd = content.length - 2;
}
setChanged(null);
}
return (
<div className={classes.container} data-testid="markdown-text-area">
<div className={classes['header']}>
<label className={classes.label} htmlFor={id}>
{label}
</label>
<Button
positive
style={{ justifySelf: 'flex-end' }}
onClick={() => setIsPreview(prev => !prev)}
>
{isPreview ? 'Edit' : 'Preview'}
</Button>
</div>
<div className={classes['text-effect-buttons']}>
<button
className={classes['text-effect-button']}
onClick={handleBoldClick}
type="button"
style={{ fontWeight: 'bold' }}
>
B
</button>
</div>
{isPreview ? (
<div className={classes['markdown-container']} id={id}>
<MarkdownParser input={content} />
</div>
) : (
<textarea
onChange={handleTextAreaChange}
className={`${classes['text-input']}${
error && touched ? ` ${classes.error}` : ''
}`}
ref={textAreaRef as MutableRefObject<HTMLTextAreaElement>}
rows={numberOfRows}
cols={numberOfCols}
onBlur={onBlur}
id={id}
value={content}
></textarea>
)}
{error && touched && (
<div className={classes['error-message']}>{error}</div>
)}
</div>
);
};
The part of the following component most relevant to answering the question is the following:
if (changed && textAreaRef.current?.value === content) {
const changeEvent = new Event('change', { bubbles: true });
textAreaRef.current?.dispatchEvent(changeEvent);
if (changed === 'bold' && textAreaRef.current) {
textAreaRef.current.focus();
textAreaRef.current.selectionStart = content.length - 2;
textAreaRef.current.selectionEnd = content.length - 2;
}
setChanged(null);
}

React - components being overwritten when deleted from state (based on there index)

I'm having trouble with creating multiple tabs form in React. Example image:
Every new tab mounts a new component. Example code:
const handleAddTab = tabIndex => {
const exampleTab = {
name: `${Date.now()} / 16.02.2022 г.`,
jsx: <Document />,
deletable: true
}
const updatedTabs = state.tabs.map((t, i) => {
if (tabIndex === i) t.subtabs.push(exampleTab)
return t
})
setState(prev => ({
...prev,
tabs: updatedTabs
}))
}
And I'm rendering those components. Example code:
{state.activeSubtabIndex === 0 ?
<Documents />
:
getScreen().subtabs.map((s, i) =>
i === 0 ?
<>
</>
:
<div
style={state.activeSubtabIndex != i ? { display: 'none' } : {}}
>
{s.jsx}
</div>
)
}
I use getScreen() to fetch the current tab and get the subtabs. Example code:
const getScreen = () => {
const typeId = query.get('type_id')
const screen = state.tabs.find(t => t.typeId == typeId)
return screen
}
The way I remove a tab is like so:
const handleCloseTab = (tabIndex, subtabIndex) => {
const updatedTabs = state.tabs.filter((t, i) => {
if (tabIndex === i) {
t.subtabs = t.subtabs.filter((ts, i) => {
return subtabIndex != i
})
}
return t
})
setState(prev => ({
...prev,
tabs: updatedTabs
}))
}
The problem is that every time I delete (for example) the first tab, the second one gets the state from the first one (based on the index it was mapped).
I solved that problem by adding an extra key-value pair (deleted: false) to the exampleTab object
const handleAddTab = tabIndex => {
const exampleTab = {
name: `${Date.now()} / 16.02.2022 г.`,
jsx: <Document />,
deletable: true,
deleted: false <====
}
const updatedTabs = state.tabs.map((t, i) => {
if (tabIndex === i) t.subtabs.push(exampleTab)
return t
})
setState(prev => ({
...prev,
tabs: updatedTabs
}))
}
Whenever I close a tab I'm not unmounting It and removing its data from the array. I'm simply checking if deleted === true and applying style={{ display: 'none' }} to both the tab and component. Example code:
{state.activeSubtabIndex === 0 ?
<Documents />
:
getScreen().subtabs.map((s, i) =>
i === 0 ?
<>
</>
:
<div
style={state.activeSubtabIndex != i || s.deleted ? { display: 'none' } : {}}
key={s.typeId}
>
{s.jsx}
</div>
)}

Why my setInterval() is speeding up inside my React image slider?

Why my interval is speeding up?
When I press any of my buttons NextImage() or PrevImage() my interval starts speeding up and the image starts glitching. Any advice or help?
Here's my code =>
//Image is displayed
const [image, setImage] = React.useState(1);
let imageShowed;
if (image === 1) {
imageShowed = image1;
} else if (image === 2) {
imageShowed = image2;
} else if (image === 3) {
imageShowed = image3;
} else {
imageShowed = image4;
}
// Auto change slide interval
let interval = setInterval(
() => (image === 4 ? setImage(1) : setImage(image + 1)),
5000
);
setTimeout(() => {
clearInterval(interval);
}, 5000);
// Change image functionality
const ChangeImage = (index) => {
setImage(index);
};
/ /Next image
const NextImage = () => {
image === 4 ? setImage(1) : setImage(image + 1);
};
// Previous image
const PrevImage = () => {
image === 1 ? setImage(4) : setImage(image - 1);
};
When you need to have some logic which is depend on changing a variable, it's better to keep those logic inside useEffect
const interval = useRef(null);
const timeout = useRef(null);
useEffect(() => {
interval.current = setInterval(
() => (image === 4 ? setImage(1) : setImage((i) => i + 1)),
5000
);
timeout.current = setTimeout(() => {
clearInterval(interval.current);
}, 5000);
return () => {
clearInterval(interval.current);
clearTimeout(timeout.current);
}
}, [image]);
one point to remember is that if you use a variable instead of using useRef it can increase the possibility of clearing the wrong instance of interval or timeout during the rerenders. useRef can keep the instance and avoid any unwanted bugs
Your approach causes so many problems and you should learn more about react (watch youtube tutorials about react), I did make a working example slider hope to help you and people in the future:
let interval;
const images = [
"https://picsum.photos/300/200?random=1",
"https://picsum.photos/300/200?random=2",
"https://picsum.photos/300/200?random=3",
"https://picsum.photos/300/200?random=4",
"https://picsum.photos/300/200?random=5",
];
const App = () => {
const [slide, setSlide] = React.useState(0);
React.useEffect(() => {
interval = setInterval(() => {
NextSlide();
clearInterval(interval);
}, 5000);
return () => {
clearInterval(interval);
};
}, [slide]);
const ChangeSlideDots = (index) => {
setSlide(index);
};
const NextSlide = () =>
setSlide((prev) => (slide === images.length - 1 ? 0 : prev + 1));
const PrevSlide = () =>
setSlide((prev) => (slide === 0 ? images.length - 1 : prev - 1));
return (
<div style={styles.root}>
<img style={styles.imageDiv} src={images[slide]} />
<button style={styles.buttons} onClick={PrevSlide}>
◁
</button>
<div style={styles.dotDiv}>
{images.map((_, i) => (
<div
key={i}
style={i === slide ? styles.redDot : styles.blackDot}
onClick={() => ChangeSlideDots(i)}
>
.
</div>
))}
</div>
<button style={styles.buttons} onClick={NextSlide}>
▷
</button>
</div>
);
}
const styles = {
root: {
display: "flex",
position: "relative",
width: 300,
height: 200,
},
buttons: {
backgroundColor: "rgb(255 255 255 / 37%)",
border: "none",
zIndex: 2,
flex: 1,
},
imageDiv: {
position: "absolute",
zIndex: 1,
width: 300,
height: 200,
},
dotDiv: {
flex: 10,
zIndex: 2,
fontSize: "30px",
display: "flex",
justifyContent: "center",
},
redDot: {
cursor: "pointer",
color: "red",
},
blackDot: {
cursor: "pointer",
color: "black",
},
};
ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Anytime that you rerender your component, you will run the whole function once. So you will set an interval every time you use setImage(). In order to prevent this, you have to use side effect functions. here you should use useEffect() because you have a functional component. in order to make useEffect() only run once, you have to pass an empty array for dependecy array; So your useEffect will act like componentDidMount() in class components. try the code below:
let interval = null
useEffect(() => {
interval = setInterval(
() => (image === 4 ? setImage(1) : setImage(image + 1)),
5000
)
setTimeout(() => {
clearInterval(interval);
}, 5000)
}, [])
Thanks, everybody for your great answers appreciated a lot your time and help!
So, my final solution looks like this:
const images = [image1, image2, image3, image4];
const quotes = [
'Jobs fill your pockets, adventures fill your soul',
'Travel is the only thing you buy that makes you richer',
'Work, Travel, Save, Repeat',
'Once a year, go someplace you’ve never been before',
];
const App = () => {
//Image is displayed
const [image, setImage] = React.useState(0);
// Auto change slide interval
useEffect(() => {
return () => {
clearInterval(
setInterval((interval) => {
image === 3 ? setImage(1) : setImage(image + 1);
clearInterval(interval.current);
}, 5000)
);
};
}, [image]);
// Change image functionality
const ChangeImage = (index) => {
setImage(index);
};
//Next image
const NextImage = () => {
image === 3 ? setImage(1) : setImage(image + 1);
};
// Previous image
const PrevImage = () => {
image === 1 ? setImage(3) : setImage(image - 1);
};
return (
<Section>
<div className='slideshow-container'>
<div>
<img className='slider_image' src={images[image]} alt='slider' />
<h1 className='slider_title'>{quotes[image]}</h1>
</div>
<button className='slider_prev' onClick={PrevImage}>
❮
</button>
<button className='slider_next' onClick={NextImage}>
❯
</button>
</div>
<div>
<div>
{images.map((image, i) => (
<img
key={i}
alt={`slider${i}`}
src={image}
className='bottom_image'
onClick={() => ChangeImage(i)}
></img>
))}
</div>
</div>
</Section>
);
};

react clearinterval not working for another function

i am working on one of the react project, for that i have used setInterval and clearInterval, it works fine in same function, but when i used clearInterval in another function it is not working, can anyone please help me why it is happening like that ? here i have attached my whole code, can anyone please look my code and help me to resolve this issue ? clearInterval is not working for the onClickStartRound in function
export class GamePlayBlack extends React.Component<Props, State>
{
public interval_counter;
public setTimer = '30:00';
public setSeconds = 30;
constructor(props: Props)
{
super(props);
this.state = {
gameData: GameDataStore.state,
userData: UserDataStore.state,
buttonLoading: false,
gameStarted: false,
updateTimer: this.setTimer,
clearintervaltimer : '',
};
localStorage.setItem('is_called','0');
localStorage.setItem('is_called_show_winner_card','0');
}
public componentDidMount(): void
{
GameDataStore.listen(data => this.setState({
gameData: data
}));
UserDataStore.listen(data => this.setState({
userData: data
}));
}
private onSelect = (winningPlayerGuid: string) =>
{
return GameDataStore.chooseWinner(this.state.userData.playerGuid, winningPlayerGuid);
};
private onClickStartRound = () =>
{
localStorage.setItem('is_started','1');
clearInterval(this.interval_counter);
this.setState({
buttonLoading: true,
gameStarted: true,
});
GameDataStore.startRound(this.state.userData.playerGuid)
.finally(() => this.setState({
buttonLoading: false,
gameStarted: true,
}));
};
private onClickSkipBlack = () =>
{
this.setState({
buttonLoading: true
});
GameDataStore.skipBlack(this.state.userData.playerGuid)
.finally(() => this.setState({
buttonLoading: false
}));
};
private waitforstart = (game_id:any,playerGUID:any,chooserGuid:any) => { //game_id:any,playerGUID:any,chooserGuid:any
console.log("guid");
console.log(this.state.gameData);
console.log(this.state.userData);
let self = this;
window.setInterval(function() {
//alert("sdsd");
return GameDataStore.removePlayer(game_id,playerGUID,chooserGuid);
}.bind(self),30000);
};
private skipPlayer = (game_string_id:any,target_turn:any,chooserGuid:any) => {
return GameDataStore.skipPlayer(game_string_id,target_turn,chooserGuid);
}
public startTimer = () => {
localStorage.setItem('is_called','1');
let timer = this.setSeconds, minutes, seconds;
let _self = this;
this.interval_counter = setInterval(function () {
let is_started = localStorage.getItem('is_started');
if(is_started == "0") {
let chooserGuid = localStorage.getItem('chooserGuid');
let game_string_id = localStorage.getItem('game_id');
let target_turn = localStorage.getItem('target_turn');
let is_called = localStorage.getItem('is_called');
if(typeof timer!==undefined && timer!=null) {
minutes = parseInt(timer/60 as any,10);
seconds = parseInt(timer%60 as any,10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
console.log(minutes + ":" + seconds);
_self.setState({
updateTimer: minutes + ":" + seconds
});
if (--timer < 0) {
//localStorage.setItem('is_called','0');
if(is_started == "0") {
_self.skipPlayer(game_string_id,target_turn,chooserGuid);
clearInterval(_self.interval_counter);
return false;
}
}
}
}
}, 1000);
}
public render()
{
const {
gameData,
buttonLoading
} = this.state;
const me = gameData.game?.players?.[this.state.userData.playerGuid];
const defsLoaded = cardDefsLoaded(gameData);
if (!me || !gameData.game || !defsLoaded)
{
return <ContainerProgress />;
} else {
let game_id = gameData.game.id;
let chooserGuid = gameData.game.chooserGuid;
let all_players = gameData.game.players;
let all_player_id = Object.keys(all_players);
//alert(gameData.game.chooserGuid);
let filteredAry = all_player_id.filter(e => e !== this.state.userData.playerGuid);
console.log("guid:"+chooserGuid);
console.log("all players:"+all_player_id);
console.log("new array:"+filteredAry);
let target_item = filteredAry.find((_, i, ar) => Math.random() < 1 / (ar.length - i));
if(typeof target_item !== undefined && target_item!=null) {
localStorage.setItem('target_turn',target_item);
}
}
const {
players,
chooserGuid,
roundCards,
roundStarted,
} = gameData.game;
const roundCardKeys = Object.keys(roundCards ?? {});
const remainingPlayerGuids = Object.keys(players ?? {})
.filter(pg => !(pg in (roundCards ?? {})) && pg !== chooserGuid);
const remainingPlayers = remainingPlayerGuids.map(pg => unescape(players?.[pg]?.nickname));
const revealedIndex = this.state.gameData.game?.revealIndex ?? 0;
const timeToPick = remainingPlayers.length === 0;
const revealMode = timeToPick && revealedIndex < roundCardKeys.length;
const hasWinner = !!gameData.game?.lastWinner;
if(!roundStarted) {
//alert(chooserGuid);
localStorage.setItem('is_started','0');
if(typeof gameData.game.id !== undefined && gameData.game.id!=null) {
localStorage.setItem('game_id',gameData.game.id);
}
if(typeof gameData.game.chooserGuid !== undefined && gameData.game.chooserGuid!=null) {
localStorage.setItem('chooserGuid',gameData.game.chooserGuid);
}
let is_called = localStorage.getItem('is_called');
if(is_called == "0") {
this.startTimer();
}
} else {
localStorage.setItem('is_started','1');
}
return (
<>
<div>
<PlayersRemaining/>
</div>
<Divider style={{margin: "1rem 0"}}/>
{!roundStarted && (
<Typography style={{marginBottom: "1rem", textAlign: "center"}}>
<strong>You are the Card Captain!</strong>
<br/>
Read the card aloud, then click Start The Round. Once everyone plays, you will choose your favorite!<br/>
<span> {this.state.updateTimer}</span> Seconds
</Typography>
)}
{!timeToPick && roundStarted && (
<CardPlayTimeRemaining gameData={gameData}/>
)}
<Grid container spacing={2} style={{justifyContent: "center", marginTop: "1rem"}}>
{(!hasWinner) && (
<Grid item xs={12} sm={6} md={4} lg={3}>
<BlackCard packId={gameData.game?.blackCard.packId}>
{gameData.blackCardDef?.content}
</BlackCard>
</Grid>
)}
<RevealWhites canReveal={true}/>
<ShowWinner/>
</Grid>
{!roundStarted && (
<div style={{marginTop: "1rem", textAlign: "center"}}>
<LoadingButton loading={buttonLoading} color={"secondary"} variant={"outlined"} onClick={this.onClickSkipBlack}>
Skip Card
</LoadingButton>
<LoadingButton loading={buttonLoading} color={"secondary"} variant={"contained"} onClick={this.onClickStartRound} style={{marginLeft: "1rem"}}>
Start the round!
</LoadingButton>
</div>
)}
<PickWinner
canPick={true}
hasWinner={hasWinner}
onPickWinner={this.onSelect}
revealMode={revealMode}
timeToPick={timeToPick}
/>
</>
);
}
}

What is wrong in this react useState hook?

I found this component online which creates a review component but it's not working.
import { useState } from 'react';
const Star = ({filled, starId}) => (
<span star-id={starId} style={{ color: '#ff9933' }} role="button">
{filled ? '\u2605' : '\u2606'}
</span>
);
export const Rating = props => (
const [rating, setRating] = useState(typeof props.rating == 'number' ? props.rating : 0);
const [selection, setSelection] = useState(0);
const hoverOver = event => {
let val = 0;
if (event && event.target && event.target.getAttribute('star-id'))
val = event.target.getAttribute('star-id');
setSelection(val);
};
return (
<div
onMouseOut={() => hoverOver(null)}
onClick={event => setRating(event.target.getAttribute('star-id') || rating)}
onMouseOver={hoverOver}
>
{Array.from({ length: 5 }, (v, i) => (
<Star
starId={i + 1}
key={`star_${i + 1} `}
filled={selection ? selection >= i + 1 : rating >= i + 1}
/>
))}
</div>
);
It throws an error for this line:
const [rating, setRating] = useState(typeof props.rating == 'number' ? props.rating : 0);
What is wrong with it? And how can be it fixed?
I think the hook is fine, but you need to use {} around the function body:
export const Rating = props => {
const [rating, setRating] = useState((typeof props.rating === 'number') ? props.rating : 0);
const [selection, setSelection] = useState(0);
const hoverOver = event => {
let val = 0;
if (event && event.target && event.target.getAttribute('star-id'))
val = event.target.getAttribute('star-id');
setSelection(val);
};
return (
<div
onMouseOut={() => hoverOver(null)}
onClick={event => setRating(event.target.getAttribute('star-id') || rating)}
onMouseOver={hoverOver}
>
{Array.from({ length: 5 }, (v, i) => (
<Star
starId={i + 1}
key={`star_${i + 1} `}
filled={selection ? selection >= i + 1 : rating >= i + 1}
/>
))}
</div>
);
};
codesandbox
A second thing you should consider: You probably should use === instead of == for the typeof check.

Resources