Pause video on bootstrap carousel slide change - reactjs

I have an array of videos which are displayed by Bootstrap carousel embedded in a modal dialog box. The video doesn't start playing automatically when the modal is show but you need to click on it and it will start playing. My issues is that on slide change the current video playing won't pause. So this is what i need to achieve, to pause current video when the user changes slides(back or forth). How can I do this?
BTW I am using React Js.
Any help is greatly valued.Thanks.✌️
Below is my Video Carousel component.
import React, { Component } from "react";
import 'bootstrap/dist/css/bootstrap.min.css';
import Carousel from 'react-bootstrap/Carousel';
import "../carousel.css";
export default class VideoCarousel extends Component {
constructor(props) {
super(props);
this.videoRef = React.createRef();
// this.state = {
// index: 0,
// isPlaying: false
// }
}
render(){
return(
<div>
<Carousel activeIndex={this.index} onSelect={this.handleChange} interval={null} className="carousel">
{this.props.items.map((item, index) => {
return (
<Carousel.Item key={index}>
<video
ref = {this.videoRef}
className="videoItem"
controls="controls"
>
<source src={item.src} type="video/mp4"/>
</video>
<Carousel.Caption>
{/* <h2>{item.title}</h2> */}
</Carousel.Caption>
</Carousel.Item>
);
})}
</Carousel>
</div>
)
}
}

Problem
You are using the same reference for all the items. After running the map function this.videoRef will be equal to the last element of the array.
Solution 1: keep the references in an array
Create the ref initialized by an empty array:
this.videoRef = React.createRef([]);
Iterate over the elements and assign the reference based on the index value:
<Carousel activeIndex={this.index} onSelect={this.handleChange} interval={null} className="carousel">
{this.props.items.map((item, index) => {
return (
<Carousel.Item key={index}>
<video
ref = {el => this.videoRef.current[index] = el}
...
>
...
</Carousel.Item>)
})}
</Carousel>
To pause the video use the element reference at specific position:
this.videoRef.current[index].pause();
Solution 2: Create a component for the carrousel item
You can handle the controls functions on componentDidMount/componentWillUnmount
this.videoRef = React.createRef(null);
Assign the reference:
export default class VideoCarouselItem extends Component {
constructor(props) {
super(props);
this.videoRef = React.createRef();
}
render(){
<Carousel.Item key={this.props.key}>
<video
ref = {this.videoRef}
...
>
</Carousel.Item>
}
}
Then through properties pass the info you need.

Related

How Do I Fix This TypeError In React?

I am struggling and in need of help! I'm extremely new to React - and I'm busy doing this practice assignment for a course that I'm taking to learn it, but I am running into the same sort of problem over and over again and I cannot seem to get it right.
I do not need you to complete my assignment, but any hint or directions/feedback on my code on where I am going wrong and what I am doing wrong would be highly appreciated. Oof, I'm just so tired of not finding the solution on Google/forums, and my React skills are fairly novice so I can't really identify the problem without the help of some super cool React experts out there! Thank you very much in advance. :)
I keep on getting the error: TypeError: Cannot read property 'map' of undefined
Full Source Code Here: https://github.com/christinec-dev/react_new
The overall end goal of my application is as follows (added here for perspective reasons):
Task 1:
A new DishdetailComponent has been added to your React application.
Included the DishDetail into your MenuComponent's view to show the selected dish.
Passing the selected dish as props to the DishDetail Component.
Used the appropriate Bootstrap classes to the card so that it occupies the entire row for xs and sm screen sizes, and 5 columns for md screens and above.
Used the appropriate Bootstrap classes to the div containing the list of comments so that it occupies the entire row for xs and sm screen sizes, and 5 columns for md screens and above.
Task 2:
Used the Card component to display the details of the dish.
Task 3:
Included a list of comments about the dish into the dishdetail view.
----------------------------------------- menuComponent.js
//package imports
import React, { Component } from 'react';
//import bootrap components from reactstrap library
//NOTE: The media query returns a CSS style
import { Card, CardImgOverlay, CardImg, CardBody, CardText, CardTitle } from 'reactstrap';
import DishDetail from './DishdetailComponent';
//creates Menu component
class Menu extends Component {
//define a constructor for it
constructor(props) {
//Props is read-only and are used to pass data, whereas state is for managing data and can be modified by its own component
//required when you create a component in react
super(props);
//when document is loaded no card has been selected by default
this.state = {
selectedDish: null
};
}
//when dishg is clicked, it will render details of dish
onDishSelect(dish) {
this.setState({selectedDish: dish});
}
//if dish is clicked it will show card details, else nothing
renderDish(dish) {
if(dish != null) {
return(
<Card>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
);
} else {
return(
<div></div>
)
}
}
//return a value or function that will be called
render() {
//will iterate (map) over the dishes list and return each key (item) uniquely
const menu = this.props.dishes.map((dish) => {
// This will create the layout of the menu items by displaying the image, name and -description- of each menu item
return (
<div key={dish.id} className="col-12 col-md-5 m-1">
{/* When clicked on, it will run event function*/}
<Card onClick={() => this.onDishSelect(dish.id, dish.comments)}>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardImgOverlay body className="ml-5">
<CardTitle>{dish.name}</CardTitle>
</CardImgOverlay>
</Card>
</div>
);
});
return (
//This will return a menu list that will be defined
<div className="container">
<div className="row">
{/* This will return the menu items */}
{menu}
</div>
<div className="row">
{/* This will return the clicked card dish items when clicked */}
<div className="col-12 col-md-5 m-1">
{this.renderDish(this.state.selectedDish)},
</div>
<div className="col-12 col-md-5 m-1">
<DishDetail dishes={this.state.dishes} />
</div>
</div>
</div>
);
}
}
//exports it for use in other files
export default Menu;
-------------------DishdetailComponent.js
//package imports
import React, { Component } from 'react';
//import bootrap components from reactstrap library
//NOTE: The media query returns a CSS style
//creates Dishdetail component
class DishDetail extends Component {
//define a constructor for it
constructor(props) {
//Props is read-only and are used to pass data, whereas state is for managing data and can be modified by its own component
//required when you create a component in react
super(props);
//when document is loaded no card has been selected by default
this.state = {
selectedDish: null,
comments: null
};
}
onDishSelect(comments) {
this.setState({comments: comments});
}
//if dish is clicked it will show card comments, else nothing
renderComments(comments) {
if (comments != null){
return (
<ul key={comments.id} className="list-unstyled">
<li className="comment">{comments.comment}</li>
<li className="author"> {comments.author}</li>
<li className="date"> {comments.date}</li>
</ul>
)
}
else {
return(
<div></div>
)
}
}
//return a value or function that will be called
render() {
//will iterate (map) over the dishes list and return each key (item) uniquely
const details = this.props.comments.map((comments) => {
return (
<div className="container">
<div className="row">
<div className="col-12 col-md-5 m-1">
<h4>Comments</h4>
<div>{comments}</div>
</div>
</div>
</div>
);
});
return (
//This will return a menu list that will be defined
<div className="container">
<div className="row">
{/* This will return the menu items */}
{details}
</div>
<div className="row">
{/* This will return the clicked card dish items when clicked */}
{this.renderComments(this.state.selectedDish)},
</div>
</div>
);
}
}
//exports it for use in other files
export default DishDetail;
----------------------------------------- App.js
//package and component imports
import logo from './logo.svg';
import React, {Component} from 'react';
import { NavbarBrand, Navbar } from 'reactstrap';
import Menu from './components/menuComponent';
import DishDetail from './components/DishdetailComponent';
import './App.css';
import {DISHES} from './shared/dishes';
//creates Menu component
class App extends Component {
//define a constructor for it
constructor (props) {
//Props is read-only and are used to pass data, whereas state is for managing data and can be modified by its own component
//required when you create a component in react
super(props);
//when document is loaded no card has been selected by default
this.state = {
dishes: DISHES,
selectedDish: null,
comments: null
};
}
//when dishg is clicked, it will render details of dish
onDishSelect(dish) {
this.setState({ selectedDish: dish});
}
render () {
return (
//To create html structures in React we always define it via the className strucutre
<div>
{/* This will create a layour based on our individual component files. For example, if we have a navbarComponent file, then we just import it from there and insert it here, without having to code the whole thing. */}
<Navbar color="primary" dark expand="md">
<div className="container">
<NavbarBrand href="/"> Ristorante Con Fusion</NavbarBrand>
</div>
</Navbar>
{/* The Menu component from the menuComponent.js file is rendered here and displayed when the index.js is loaded */}
<Menu dishes={this.state.dishes} />
</div>
);
}
}
//exports it for use in other files
export default App;
The error is preatty clear, this.props.comments or this.props.dishes are undefined.
You are trying to access the property .map of undefined element.
Basically add a check to be sure that this.props.comments and this.props.dishes exist and are arrays, or you can add a loader or you can add question point this.props.comments?.map(... and this.props.dishes?.map(...
I was able to solve it by myself after reading some documentation. It turns out I had to remove the renderDish(dish) function from the menuComponent.js file and place it in the DishdetailComponent.js file.
To solve the
TypeError: Cannot read property 'map' of undefined problem
I did the following:
//if dish is clicked it will show dish comments, else nothing
renderComments(array) {
//if dish array is not equal to null, display comments in half grid
if (array.length != 0) {
return (
//col-12 for sm and xs, and col-5 for md and lg screens with margin # 1
<div className="col-12 col-md-5 m-1">
{/* Displays the comment title, and details of author, date and comment */}
<h4>Comments</h4>
{/* //will iterate (map) over the comments list and return each key (item) uniquely */}
{ array.map (comment => (
<ul className="list-unstyled">
<li>
<p>{comment.comment}</p>
<p><i> - {comment.author} </i>, {comment.date}</p>
</li>
</ul>
))
}
</div>
);
}
//if the dish contains nothing, show nothing
else {
return (
<div></div>
);
}
}
src\App.js Line 4:8: 'DishDetail' is defined but never used no-unused-vars
Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.

Props is not showing something on the screen

I am trying to show the number 0 on the screen using props. However nothing shows on the screen and I am not sure why. This is the code:
import React,{Component} from 'react';
import Toolbar from './Toolbar.js';
class Counter extends Component {
constructor(props) {
super(props);
this.state ={
counter:0
}
};
render() {
return(
<div>
{this.state.counter.map(count=>(
<Toolbar count={count}/>
))}
</div>
)
}
};
export default Counter;
And this is where I called it
<div className="toolbar__cart">
<span>{props.count}</span>
<img src="Images/basket.png" alt="Basket" width="40"/></div>
I don't think the map function is applicable here since the value of counter is an integer and not an array. See here for more info on the map function.
If you just want your Toolbar component to display the value of this.state.counter, you could use this:
return(
<Toolbar count={this.state.counter}></Toolbar>
)
And then your Toolbar component would use that counter value like this:
function Toolbar(props) {
return (
<div>
<span>{props.count}</span>
<img src="Images/basket.png" alt="Basket" width="40"/>
</div>
)
}
You are using map function incorrectly.
.map is a function used along with an array
Check out : https://www.w3schools.com/jsref/jsref_map.asp
Correct Code :
render() {
return (
<div>
<Toolbar count={this.state.counter} />
</div>
);
}
You can checkout full code here : https://codesandbox.io/s/naughty-lederberg-hb4wp?file=/src/App.js:203-309
counter.map map function for Arrays - sample
import React,{Component} from 'react';
import Toolbar from './Toolbar.js';
class Counter extends Component {
constructor(props) {
super(props);
this.state ={
counter:[0,1,2]
}
};
render() {
return(
<div>
{this.state.counter.map(count=>(
<Toolbar count={count}/>
))}
</div>
)
}
};

Unable to make react-player light prop always true

I have implemented a slider like property. Inside each slide, there is a video and its name. I am using [react-player][1] to display the video thumbnail. Once you click on any of the video a modal will get open and will play the video I have rendered the react-player is that the light property is always true. But it's not working once you click on the video that particular position of the slider loses the light property.
import React, { Component } from 'react'
import IndividualSlider from './IndividualSlider'
import ModalVideo from 'react-modal-video'
import { Modal, Button } from 'antd';
import ReactPlayer from 'react-player/youtube';
export class Experience extends Component {
constructor(){
super();
this.state={
Video:[
{
url:'https://www.youtube.com/embed/H2yCdBIpxGY',
name:'Recurssion'
},
{
url:'https://www.youtube.com/embed/s5YgyJcoUI4',
name:'Array'
},
{
url:'https://www.youtube.com/embed/_C4kMqEkGM0',
name:'DP'
},
{
url:'https://www.youtube.com/embed/VBnbYNksWTA',
name:'Graph'
},
{
url:'https://www.youtube.com/embed/M1q3Pzk2UXs',
name:'Trie'
}
],
modalIsOpen:false,
modalLink:""
}
this.left = this.left.bind(this);
this.right=this.right.bind(this);
this.modalPlay = this.modalPlay.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.handleOk = this.handleOk.bind(this);
}
handleOk = e => {
console.log(e);
this.setState({
modalIsOpen: false,
});
};
handleCancel = e => {
console.log(e);
this.setState({
modalIsOpen: false,
});
};
modalPlay=(link)=>{
this.setState({
modalIsOpen:true,
modalLink:link
})
}
right=()=>{
let arr = this.state.Video;
let temp = arr[0];
arr.shift();
arr.push(temp);
this.setState({
Video:arr
})
}
left=()=>{
let arr = this.state.Video;
let temp = arr[arr.length-1];
arr.pop();
arr.unshift(temp);
this.setState({
Video:arr
})
}
render() {
return (
<div className="ExperienceAClass">
<div className="OneWeekHeading">
<h2 className="OneWeekCaption">
Experience a class
</h2>
<hr className="MentorsCaptionUnderLine" align="center" width="50%"></hr>
</div>
<Modal
title=""
visible={this.state.modalIsOpen}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={null}
>
<ReactPlayer className="ModalVideo" url={this.state.modalLink}/>
</Modal>
<div className="EntireSliderWrapper">
<a class="prev" onClick={this.left}>
</a>
<div className="VideoSlider">
{this.state.Video.map((child,index)=>{
return <IndividualSlider modalPlay={this.modalPlay} url={child.url} name=
{child.name}/>
})
}
</div>
<a class="next" onClick={this.right}>
</a>
</div>
</div>
)
}
}
export default Experience
And the IndividualSlider component is as follows:
import React, { Component } from 'react'
import ReactPlayer from 'react-player/youtube';
export class IndividualSlider extends Component {
constructor(){
super();
this.state={
light:true
}
this.onClick=this.onClick.bind(this)
}
onClick=()=>{
let modalPlay=this.props.modalPlay;
modalPlay(this.props.url);
}
render() {
return (
<div className="VideoDetails fade">
<ReactPlayer className="YoutubeVideo" onClick={this.onClick} light =
{this.state.light} url={this.props.url}/>
<p>
{this.props.name}
</p>
</div>
)
}
}
export default IndividualSlider
In the above code, I have made the light prop to be always true. As the slide is clicked this component renders but the thumbnail property is not held.
Also, When the modal closes the video keeps on playing. How to deal with that?
As you can see the video which was played regain it's light={true} once slid but the position where it was when played doesn't imply light={true}
I believe the problem is that because you have two instances of React player, one has the light property passed to it and the other is not. Since you always want the light property to work, pass it as a prop set to true always.
The light property is working in the individual slider, because you are indeed setting it in the individual slider
// Inside IndividualSlider component you have this
<ReactPlayer className="YoutubeVideo" onClick={this.onClick} light =
{this.state.light} url={this.props.url}/>
// remove state it and make it like this, since state is not needed
<ReactPlayer className="YoutubeVideo" onClick={this.onClick} light={true} url={this.props.url}/>
Now you are losing the light when clicking on an individual video, because that video is rendered in the parent component (the Experience component_, and there you are not passing the light prop
// In Experience Component you have this
<ReactPlayer className="ModalVideo" url={this.state.modalLink} />
// change it to
<ReactPlayer className="ModalVideo" url={this.state.modalLink} light={true} />

How to apply MathJax/KaTex to render a React component

I am making a web editor using React & SlateJS. There are LaTex code in the editor content and I want the user to see the rendered LaTex equations. MathJax and KaTex have auto-rendering feature by loading them as CDNs. Once they are loaded, the content on html body is rendered. But they are not live-rendering when I modify the content.
So I have made a button that opens a modal which renders the un-editable edior content in a smaller window, and I want the LaTex codes to be rendered in the modal.
The APP component:
import {Editor} from 'slate-react';
import ReactModel from 'react-modal';
import RenderedEditorDialog from "./RenderedEditorDialog";
class APP extends React.component {
...
render() {
return (
<div className={"editorContainer"}>
<div className={"editor"}>
<Editor
autoFocus
ref={this.ref}
value={this.state.value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
renderMark={this.renderMarks}
renderBlock={this.renderBlock}
/>
</div>
<ReactModal
isOpen={this.state.showMathDialog}
contentLabel="Rendered content"
onRequestClose={this.handleCloseMathDialog}
>
<button onClick={this.handleCloseMathDialog}>Close Dialog</button>
<RenderedEditorDialog value={this.state.value}/>
</ReactModal>
</div>
)
}
}
RenderedEditorDialog (modal) component:
import {Editor} from 'slate-react';
class RenderedEditorDialog extends React.Component {
// eslint-disable-next-line no-useless-constructor
constructor(props) {
super(props);
}
render() {
return (
<div>
<Editor
value={this.props.value}
renderMark={this.renderMarks}
renderBlock={this.renderBlock}/>
</div>
)
}
}
My question is how I can apply MathJax/KaTex to render the content in RenderedEditorDialog component?
Thanks in advance!
KaTeX can be applied to individual DOM elements on demand, instead of all at once, by calling renderMathInElement when desired. Calling this from componentDidUpdate should do the trick:
import {Editor} from 'slate-react';
class RenderedEditorDialog extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
render() {
return (
<div ref={this.ref}>
<Editor
value={this.props.value}
renderMark={this.renderMarks}
renderBlock={this.renderBlock}/>
</div>
)
}
componentDidUpdate() {
renderMathInElement(this.ref.current, katexOptions);
}
}
I'm more comfortable with hook-based components instead of classes, which would look like this:
function RenderedEditorDialog(props) {
const ref = useRef();
useEffect(() => {
renderMathInElement(ref.current, katexOptions);
});
return (
<div ref={ref}>
<Editor
value={props.value}
renderMark={props.renderMarks}
renderBlock={props.renderBlock}/>
</div>
)
};
I'm not sure whether you want this on RenderedEditorDialog or another more specific component, but this should give you the idea. For speed, you want to apply renderMathInElement to the smallest container that contains the updated math.

Send to child which component child should render - React js

I have a <div> of multiple <input>, and an onClick event to open a modal window, which has to render certain div of inputs for closer zoom, but I am using this modal window for rendering a numpad too.
Is there any way to distinguish which component should be rendered? Is there a possibility to send certain component to modal component(child) and then render this component? I tried something like this -
<Modal update={this.editValue.bind(this)}>{Numpad}</Modal>
or
<Modal child={Numpad} update={this.editValue.bind(this)}/>
and then in Modal(child)
{React.cloneElement(this.props.children, { ...others})}
but it doesn't work, throwing invalid element type error. I can just use switch, inside render component of Modal to distinguish which component to render with props.type, but I want more simply way to do it, any tips?
As far as I understand you correctly, you try to create a tabbed interface within a modal window, so that you could show different content depending on currently active tab. I can see the following solution for that problem.
First, we create the basic component that will hold Modal component and its content:
class App extends React.Component {
render() {
return (
<Modal>
<div>The content of the first tab</div>
<div>The content of the second tab</div>
<div>The content of the third tab</div>
</Modal>
);
}
}
Each child (div) component above represents the content for each tab in Modal.
Now let's create Modal component itself:
class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: 0
};
}
switchTabs(tabIndex) {
this.setState({
tabIndex
});
}
render() {
return (
<div>
<button onClick={this.switchTabs.bind(this, 0)}>1</button>
<button onClick={this.switchTabs.bind(this, 1)}>2</button>
<button onClick={this.switchTabs.bind(this, 2)}>3</button>
<div>
{React.Children.map(this.props.children, (child, index) =>
index === this.state.tabIndex && child
)}
</div>
</div>
)
}
}
We keep the index of active tab (tabIndex) as a part of component's local state. We also show three buttons for switching between these tabs.
Finally, we iterate over components children using React.Children.map and show that child whose index corresponds to current tabIndex.
Try the snippet below.
class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: 0
};
}
switchTabs(tabIndex) {
this.setState({
tabIndex
});
}
render() {
return (
<div>
<button onClick={this.switchTabs.bind(this, 0)}>1</button>
<button onClick={this.switchTabs.bind(this, 1)}>2</button>
<button onClick={this.switchTabs.bind(this, 2)}>3</button>
<div>
{React.Children.map(this.props.children, (child, index) =>
index === this.state.tabIndex && child
)}
</div>
</div>
)
}
}
class App extends React.Component {
render() {
return (
<Modal>
<div>
<img src="https://avatars2.githubusercontent.com/u/4226954?v=3&s=400" height="200" />
</div>
<div style={{ height: '200px', width: '200px', marginTop: '5px' }}>The second tab</div>
<div>
<img src="https://avatars3.githubusercontent.com/u/810671?v=3&s=400" height="200" />
</div>
</Modal>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Resources