react change slide in slider after clicking a button - reactjs

I have a Sidebar that I've implemented this way:
import React from "react";
import './MySidebar.scss';
import {slide as Menu } from 'react-burger-menu'
export class MySidebar extends React.Component {
constructor(props) {
super(props);
}
changeSlide = (e) => {
console.log("Clicked " + e.currentTarget.id); //get text content of
}
render() {
return (
<Menu customCrossIcon={false}>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project1">Project 1</button>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project2">Project 2</button>
</Menu>
);
}
}
Then I have a component called ProjectSliderComponent that realizes the behaviour of a carousel. It is done this way:
import React from "react";
import Slider from "react-slick";
import './project-carousel.component.css';
import { ProjectComponent } from '../project/project.component';
import { LoggerService } from '../../services/logger-service';
import { appConfig } from '../../config';
export class ProjectSliderComponent extends React.Component {
state = {
activeSlide: 0,
timestamp: Date.now()
}
constructor(props) {
super(props);
this.logger = new LoggerService();
this.settings = {
dots: appConfig.dots,
arrows: false,
adaptiveHeight: true,
infinite: appConfig.infinite,
speed: appConfig.speed,
autoplay: appConfig.autoplay,
autoplaySpeed: appConfig.autoplaySpeed,
slidesToShow: 1,
slidesToScroll: 1,
mobileFirst: true,
className: 'heliosSlide',
beforeChange: (current, next) => {
this.setState({ activeSlide: next, timestamp: Date.now() });
}
};
}
render() {
let i = 0;
return (
<div>
<Slider ref = {c => (this.slider = c)} {...this.settings}>
{
this.props.projectListId.data.map(projectId =>
<ProjectComponent key={projectId} id={projectId} time={this.state.timestamp} originalIndex={ i++ } currentIndex = {this.state.activeSlide}></ProjectComponent>)
}
</Slider>
</div>
);
}
}
The ProjectComponent component simply specifies the layout. This is the App.js file, where I load the projectslidercomponent and my sidebar. I do this:
class App extends Component {
state = {
projectList: new ProjectListModel(),
isLoading: true
}
constructor(props) {
super(props);
this.logger = new LoggerService();
}
componentDidMount() {
let projectService = new ProjectService();
projectService.getProjectList()
.then(res => {
let projectList = new ProjectListModel();
projectList.data = res.data.data;
this.setState({ projectList: projectList,
isLoading: false });
})
.catch(error => {
this.logger.error(error);
});
}
render() {
return (
<div className="App">
<MySidebar>
</MySidebar>
<ProjectSliderComponent projectListId={this.state.projectList}></ProjectSliderComponent>
</div>
);
}
}
export default App;
What I need to do is to change the slide according to which button I clicked. What do I have to do? Is there something like passing the "instance" of the projectslidercomponent and call a method to change the slide? Or something else?

At the react-slick docs you can read about slickGoTo, which is a method of the Slider component.
Since you're already storing this.slider you can try to make it accessible in MySidebar and use it whenever changeSlide is called. Most likely you have to create a changeSlide method on top level and hand it as property to your components.

To sort this problem what you have to do is create a function in parent component which updates the state of the app component and once the state is updated it will re render app component and the new props are send to slider component which will tell which slider to show. Below is the code :-
In App.js
class App extends Component {
state = {
projectList: new ProjectListModel(),
isLoading: true,
menuId: 0
}
constructor(props) {
super(props);
this.logger = new LoggerService();
}
componentDidMount() {
let projectService = new ProjectService();
projectService.getProjectList()
.then(res => {
let projectList = new ProjectListModel();
projectList.data = res.data.data;
this.setState({ projectList: projectList,
isLoading: false });
})
.catch(error => {
this.logger.error(error);
});
}
ChangeSlide = (menuId) => {
this.setState({
menuId //Here you will receive which slide to show and according to that render slides in mySliderbar component
})
}
render() {
return (
<div className="App">
<MySidebar ChangeSlide={this.ChangeSlide} />
<ProjectSliderComponent menuId={this.state.menuId} projectListId={this.state.projectList}></ProjectSliderComponent>
</div>
);
}
}
export default App;
In mySlidebar
import React from "react";
import './MySidebar.scss';
import {slide as Menu } from 'react-burger-menu'
export class MySidebar extends React.Component {
constructor(props) {
super(props);
}
changeSlide = (e) => {
console.log("Clicked " + e.currentTarget.id); //get text content of
this.props.changeSlide(e.currentTarget.id)
}
render() {
return (
<Menu customCrossIcon={false}>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project1">Project 1</button>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project2">Project 2</button>
</Menu>
);
}
}
In slider component, you have to listen when the new props are coming and according to that change the slide see componentWillReceiveProps
import React from "react";
import Slider from "react-slick";
import './project-carousel.component.css';
import { ProjectComponent } from '../project/project.component';
import { LoggerService } from '../../services/logger-service';
import { appConfig } from '../../config';
export class ProjectSliderComponent extends React.Component {
state = {
activeSlide: 0,
timestamp: Date.now()
}
constructor(props) {
super(props);
this.logger = new LoggerService();
this.settings = {
dots: appConfig.dots,
arrows: false,
adaptiveHeight: true,
infinite: appConfig.infinite,
speed: appConfig.speed,
autoplay: appConfig.autoplay,
autoplaySpeed: appConfig.autoplaySpeed,
slidesToShow: 1,
slidesToScroll: 1,
mobileFirst: true,
className: 'heliosSlide',
beforeChange: (current, next) => {
this.setState({ activeSlide: next, timestamp: Date.now() });
}
};
}
componentWillReceiveProps(nextProps) {
if(nextprops.menuId != this.props.menuId){
this.slider.slickGoTo(nextprops.menuId, true)//use slider instance to
//call the function to go to a particular slide.
}
}
render() {
let i = 0;
const { menuId } = this.props
return (
<div>
<Slider initialSlide={menuId} ref = {c => (this.slider = c)} {...this.settings}>
{
this.props.projectListId.data.map(projectId =>
<ProjectComponent key={projectId} id={projectId} time={this.state.timestamp} originalIndex={ i++ } currentIndex = {this.state.activeSlide}></ProjectComponent>)
}
</Slider>
</div>
);
}
}

Related

Changing state between two child class components in React

I have two class components: Title and PlayButton. By default, the Title is programmed to change images when it is being hovered over but I would like it so that when the Playbutton is hovered over, the Title also changes its image (changes the state of the image). How would I go about this? I know I should use a parent component that handles the state of both its "children" (the Title and the PlayButton), but since I'm new to react, I'm not sure how.
Any assistance would be appreciated, thank you!
Code for Title:
import React from 'react'
import './Title.css'
import playHoverProvider from './playHoverProvider'
class Title extends React.Component {
constructor(props) {
super(props);
this.state = {
imgSrc: require('./oglogo'),
control: require('./oglogo')
};
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleMouseOut = this.handleMouseOut.bind(this);
}
handleMouseOver() {
this.setState({
imgSrc: require('./difflogo')
});
}
handleMouseOut() {
this.setState({
imgSrc: require('./oglogo')
});
}
render() {
return (
<div className='logo'>
<view>
<img onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} src={this.state.imgSrc}
style={{width: 800,
flex: 1,
height: null,
}}
alt = 'Logo' />
</view>
</div>
);
}
}
Title.propTypes = {
}
Title.defaultProps = {
}
export default Title;
Code for PlayButton:
import { hover } from '#testing-library/user-event/dist/hover';
import React from 'react'
import './PlayButton.css';
class PlayButton extends React.Component {
constructor(props) {
super(props);
this.state = {
imgSrc: require('./playbutton.png'),
disabled: false
};
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleMouseOut = this.handleMouseOut.bind(this);
}
// On-click
handleClick = (event) => {
if (this.state.disabled) {
return;
}
this.setState({
disabled: true
});
}
handleMouseOver () {
this.setState({
imgSrc: require('./playbuttonblue.png')
});
}
handleMouseOut () {
this.setState({
imgSrc: require('./playbutton.png')
});
}
render() {
return (
<div className='playbutton'>
<a href='./Rule'>
<button className='buttonprop' onClick={this.handleClick} disabled={this.state.disabled}>
{this.state.disabled ? '' :
<img onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
src={this.state.imgSrc} width = {100} height = {50} alt = 'Play'/>}
</button>
</a>
</div>
);
}
}
PlayButton.propTypes = {
}
PlayButton.defaultProps = {
}
export default PlayButton

react twilio video: first joiner screen black on participant join

i have made a twillio video app.i can show local video on Laptop website but when i join from another chrome tab or mobile phone chrome browser the video on laptop goes black and only one video is showing whereas both videos should show properly.i am following this tutorial
https://www.twilio.com/blog/build-a-custom-video-chat-app-with-react-and-twilio-programmable-video
here is my code
App.js
import './App.scss';
import React, {Component} from 'react';
import Room from './Components/Room';
const { connect } = require('twilio-video');
const Token = {"identity":"Jose Corkery","token":"...sioAMt4..."}
class App extends Component {
constructor(props) {
super(props)
this.state = {
identity: '',
room: null
}
this.inputRef = React.createRef();
this.joinRoom = this.joinRoom.bind(this);
this.returnToLobby = this.returnToLobby.bind(this);
this.updateIdentity = this.updateIdentity.bind(this);
this.removePlaceholderText = this.removePlaceholderText.bind(this)
}
async joinRoom() {
try {
// const response = Token
// const data = await response.json();
const room = await connect(Token.token, {
name: 'cool-room',
audio: true,
video: true
});
// alert(room)
this.setState({ room: room });
} catch(err) {
alert(err);
}
}
updateIdentity(event) {
this.setState({
identity: event.target.value
});
}
returnToLobby() {
this.setState({ room: null });
}
removePlaceholderText() {
this.inputRef.current.placeholder = '';
}
render() {
const disabled = this.state.identity === '' ? true : false;
return (
<div className="app">
{
this.state.room === null
? <div className="lobby">
<input
ref={this.inputRef}
onClick={this.removePlaceholderText}
placeholder="What's your name?"
onChange={this.updateIdentity}
/>
<button disabled = {disabled} onClick={this.joinRoom}>Join Room</button>
</div>
: <Room returnToLobby={this.returnToLobby} room={this.state.room} />
}
</div>
);
}
}
export default App;
Room.jsx
import React, { Component } from 'react';
import Participant from './Participant';
const { connect } = require('twilio-video');
class Room extends Component {
componentDidMount() {
this.props.room.on('participantConnected', participant => this.addParticipant(participant));
this.props.room.on('participantDisconnected', participant => this.removeParticipant(participant));
window.addEventListener("beforeunload", this.leaveRoom);
}
componentWillUnmount() {
this.leaveRoom();
}
addParticipant(participant) {
console.log(`${participant.identity} has joined the room.`);
alert(`+ Participant : ${participant.identity}`)
this.setState({
remoteParticipants: [...this.state.remoteParticipants, participant]
})
}
removeParticipant(participant) {
alert(`Leaving : ${participant.identity}`)
console.log(`${participant.identity} has left the room`);
this.setState({
remoteParticipants: this.state.remoteParticipants.filter(p => p.identity !== participant.identity)
});
}
leaveRoom() {
this.props.room.disconnect();
this.props.returnToLobby();
}
constructor(props) {
super(props)
this.state = {
remoteParticipants: Array.from(this.props.room.participants.values())
}
this.leaveRoom = this.leaveRoom.bind(this);
}
render() {
return (
<div className="room">
<div className="participants">
<Participant
key={this.props.room.localParticipant.identity}
localParticipant="true"
participant={this.props.room.localParticipant} />
{
this.state.remoteParticipants.map(participant =>
<Participant key={participant.identity} participant={participant} />
)
}
</div>
<button id="leaveRoom" onClick={this.leaveRoom}>Leave Room</button>
</div>
);
}
}
export default Room
Participant.jsx
import React, { Component } from 'react';
import Track from './Track';
const { connect } = require('twilio-video');
class Participant extends Component {
componentDidMount() {
if (!this.props.localParticipant) {
this.props.participant.on('trackSubscribed', track => this.addTrack(track));
}
}
constructor(props) {
super(props);
const existingPublications = Array.from(this.props.participant.tracks.values());
const existingTracks = existingPublications.map(publication => publication.track);
const nonNullTracks = existingTracks.filter(track => track !== null)
this.state = {
tracks: nonNullTracks
}
}
addTrack(track) {
this.setState({
tracks: [...this.state.tracks, track]
});
}
render() {
return (
<div className="participant" id={this.props.participant.identity}>
<div className="identity">{this.props.participant.identity}</div>
{
this.state.tracks.map(track =>
<Track key={track} filter={this.state.filter} track={track}/>)
}
</div>
);
}
}
export default Participant
Track.jsx
import React, { Component } from 'react';
class Track extends Component {
componentDidMount() {
if (this.props.track !== null) {
const child = this.props.track.attach();
this.ref.current.classList.add(this.props.track.kind);
this.ref.current.appendChild(child)
}
}
constructor(props) {
super(props)
this.ref = React.createRef();
}
render() {
return (
<div className="track" ref={this.ref}>
</div>
)
}
}
export default Track
demo:https://android-anime.web.app
i have only two video events onJoin and onLeave do i need additional events ?
what is the solution? if your solution works i will award you best answer.Thanks !!

Show a Simple Animation on setState in react

I display a data from JSON file to the DIV in my component.
I Set timeout for few seconds and after that the data displays.
I want to show a simple animation when the state changes to true.
Here is a sample of my Code Structure:
import someData from "../Data";
export default class Example extends Component {
constructor(props) {
super(props);
this.state = { contentLoad: false }
}
componentDidMount() {
setTimeout(() => {
this.setState({
contentLoad: true
})
}, 2500)
}
render() {
return (
<div>
{someData.map((someData) => {
return (
<div> {this.state.contentLoad && someData.name}</div>
)
})}
</div>
)
}
}
I read about react transition group, but cant understand cause i'm new to react. someone please use this as a template and provide me a codepen or codesandbox link for solution.
I agree with #JohnRuddell that Pose is a heavy library if all you need is a simple fade in. However I would look it if you are doing multiple animations that are more complex.
Sample code:
import React from "react";
import ReactDOM from "react-dom";
import posed from "react-pose";
import "./styles.css";
const AnimatedDiv = posed.div({
hidden: { opacity: 0 },
visible: { opacity: 1 }
});
class Example2 extends React.Component {
constructor(props) {
super(props);
this.state = { contentLoad: false };
}
componentDidMount() {
setTimeout(() => {
this.setState({
contentLoad: true
});
}, 2500);
}
someData = ["hello", "hi", "test"];
render() {
return (
<AnimatedDiv pose={this.state.contentLoad ? "visible" : "hidden"}>
{this.someData.map(t => {
return <div>{t}</div>;
})}
</AnimatedDiv>
);
}
}
ReactDOM.render(<Example2 />, document.getElementById("root"));
Sandbox link

How to setState in ComponentDidMount

I have something strange with a react app,
I used it in a django project and want to re-use it in a laravel project but it doesn't want to work properly ...
Here is the code of my component :
import React from "react"
import {LeftControl, RightControl, CountControl } from './controls'
import {Slide} from './slide'
import axios from "axios"
export default class Slider extends React.Component {
constructor(props){
super(props);
this.state = {
items : [],
active:0
}
}
componentDidMount() {
axios.get('/coming')
.then((res)=>{
this.setState({ items: res.data, active: 0})
});
setInterval( () => {
this.goToNextSlide()
},5000);
}
goToPrevSlide = () => {
const n = this.state.items.length
if (this.state.active == 0) {
this.setState({active : n-1})
} else {
this.setState({active: this.state.active - 1})
}
}
goToNextSlide = () => {
const n = this.state.items.length
if (this.state.active == n-1){
this.setState({active : 0})
} else {
this.setState({active: this.state.active +1})
}
}
render(){
return(
<div className="slider">
<div className="slider__controls">
<CountControl active={this.state.active} length={this.state.items.length} />
<LeftControl goToPrevSlide={this.goToPrevSlide} />
<RightControl goToNextSlide={this.goToNextSlide}/>
</div>
<div className="slider__items">
{
this.state.items
.map((item, i) => (
<Slide active={this.state.active} index={i} key={i} id={item.id} first_name={item.first_name} last_name={item.last_name} role={item.role} conference_date={item.conference_date} thumbnail={item.thumbnail} />
))
}
</div>
</div>
)
}
}
Uncommenting the setState in componentDidMount raise the following error :
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in Slider
The component works well on my other project ...
Anyone would have an idea what is the problem ?
Thank you
As riwu commented, you get the warning because the axios call and timer you define in componentDidMount try to set the state of Slider after it has been unmounted. Do the following instead:
export default class Slider extends React.Component {
...
constructor(props) {
super(props);
this._isMounted = false;
this.state = {
items : [],
active:0,
}
}
componentDidMount() {
this._isMounted = true;
axios.get('/coming')
.then((res) => {
if (this._isMounted) {
this.setState({ items: res.data, active: 0})
}
});
this.timer = setInterval(() => {
this.goToNextSlide();
}, 5000);
}
componentWillUnmount() {
this._isMounted = false;
clearInterval(this.timer);
}
...
}

ReactJS - Loading undesirable repeated data from the API

I'm fetching Data from a Dribbble API. It is almost succefully but it is loading 12 shots from the API, and after this it is occurring a problem by repeating the same 12 again and then it after this it is loading the others in a right way: 13,14,15...
The shots' return:
1,2,3,4,5,6,7,8,9,10,11,12.
OK RIGHT
1,2,3,4,5,6,7,8,9,10,11,12.
repetition problem!!! WRONG
13,14,15,16... OK RIGHT
What is the solution for this undesirable repetition?
Here is the code:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Waypoint from 'react-waypoint';
import Dribbble from './Dribbble';
export default class Dribbbles extends Component {
constructor(props) {
super(props);
this.state = { page: 1, shots: [] };
this.getShots = this.getShots.bind(this);
}
componentDidMount() {
this.getShots();
}
getShots() {
return $.getJSON('https://api.dribbble.com/v1/shots?page='+ this.state.page + '&access_token=ACCESS_TOKEN_HERE&callback=?')
.then((resp) => {
var newShots = this.state.shots.concat(resp.data);
this.setState({
page: this.state.page + 1,
shots: newShots
});
});
}
render() {
const shots = this.state.shots.map((val, i) => {
return <Dribbble dados={val} key={i} />
});
return (
<div>
<ul>{shots}</ul>
<Waypoint
onEnter={this.getShots}
/>
</div>
);
}
}
Why
I think the onEnter event from your Waypoint component is triggered unexpectedly, which has caused the getShots() function being called multiple times.
How
My suggestion is to specify a dataFecthed state to control whether the Waypoint component mount or not.Just like below:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Waypoint from 'react-waypoint'
import Dribbble from './Dribbble'
export default class Dribbbles extends Component {
constructor(props) {
super(props)
this.state = { page: 1, shots: [], dataFetched: true }
this.getShots = this.getShots.bind(this)
}
componentDidMount() {
this.getShots()
}
getShots() {
this.setState({
dataFetched: false,
})
return $.getJSON(
'https://api.dribbble.com/v1/shots?page=' +
this.state.page +
'&access_token=41ff524ebca5e8d0bf5d6f9f2c611c1b0d224a1975ce37579326872c1e7900b4&callback=?'
).then(resp => {
var newShots = this.state.shots.concat(resp.data)
this.setState({
page: this.state.page + 1,
shots: newShots,
dataFetched: true,
})
})
}
render() {
const shots = this.state.shots.map((val, i) => {
return <Dribbble dados={val} key={i} />
})
return (
<div>
<ul>{shots}</ul>
{this.state.dataFetched && <Waypoint onEnter={this.getShots} />}
</div>
)
}
}

Resources