How to play video in react - reactjs

I have a play/pause button for every video.
When I click on the play button, the last video is always played, and the icon changes on all videos. I try to do that with refs and play() method but every time, whatever video the user selects just the last video is played. Every click event play the last one.
Also, the code for full screen does not work.
This is my code:
class Video extends React.Component {
constructor(props) {
super(props);
this.state = {
playing: false,
videoList: [
{
src: 'https://clips.vorwaerts-gmbh.de/VfE_html5.mp4',
type: "video/mp4"
},
{
src: "https://clips.vorwaerts-gmbh.de/VfE_html5.mp4",
type: "video/mp4"
},
{
src: "https://clips.vorwaerts-gmbh.de/VfE_html5.mp4",
type: "video/mp4"
},
{
src: "https://clips.vorwaerts-gmbh.de/VfE_html5.mp4",
type: "video/mp4"
}
]
}
}
onPlayPauseClick = (index) => (event) => {
this.setState({
playing: !this.state.playing
});
this.state.playing ? this.video.pause() : this.video.play();
}
// onFullScreenClick = (video) => {
// this.setState({ video: video })
// if (video.requestFullscreen) {
// video.requestFullscreen();
// } else if (video.webkitRequestFullscreen) {
// video.webkitRequestFullscreen();
// } else if (video.mozRequestFullscreen) {
// video.mozRequestFullscreen();
// } else if (video.msRequestFullscreen) {
// video.msRequestFullscreen();
// }
// }
renderList = () => {
const { playing } = this.state;
return this.state.videoList.map((item, index) => {
return (
<li key={`item_${index}`}>
<video ref={(video) => { this.video = video; }} src={item.src}></video>
<img
src={playing ? "https://icon2.kisspng.com/20180419/pyq/kisspng-computer-icons-arrow-triangle-play-icon-5ad83452103159.1624767815241186100663.jpg" : "https://cdn2.iconfinder.com/data/icons/flat-and-simple-pack-2/512/1_Control_pause-512.png"}
className="play"
onClick={this.onPlayPauseClick(index)}
/>
<img src="https://cdn3.iconfinder.com/data/icons/google-material-design-icons/48/ic_fullscreen_exit_48px-512.png" className="full" />
</li>
)
});
}
render() {
return (
<div>
<ul>
{this.renderList()}
</ul>
</div>
);
}
}
class Buttons extends React.Component {
render() {
return (
<div>
<Video />
</div>
);
}
}
ReactDOM.render(<Video />, document.getElementById('app'));

It's happened because you saved the last video item in this.video, after iterating through videoList array elements . Try to save ref in this['video_'+index]=video instead of this.video=video, and start to play with code this['video_'+index].play()

Hey I think you have your ref messed up, you can create a new array of ref and use it with the index
constructor () {
this.ref = [];
}
and in your return do something like this
return this.state.videoList.map((item, index) => {
return (
<li key={`item_${index}`}>
<video ref={(video) => { this.ref.push(video) }} src={item.src}></video>
<img
src={playing ? "https://icon2.kisspng.com/20180419/pyq/kisspng-computer-icons-arrow-triangle-play-icon-5ad83452103159.1624767815241186100663.jpg" : "https://cdn2.iconfinder.com/data/icons/flat-and-simple-pack-2/512/1_Control_pause-512.png"}
className="play"
onClick={this.onPlayPauseClick(index)}
/>
<img src="https://cdn3.iconfinder.com/data/icons/google-material-design-icons/48/ic_fullscreen_exit_48px-512.png" className="full" />
</li>
)
});
and then you can call your ref inside the play pause method
onPlayPauseClick = (index) => (event) => {
this.setState({
playing: !this.state.playing
});
this.state.playing ? this.ref[index].pause() : this.ref[index].play();
}
For fullscreen I can suggest you don't try to over complicate the things, there is an awesome library for the player you can go with it.
https://www.npmjs.com/package/react-player

Full functioning code for your question.
import React from "react";
class Video extends React.Component {
constructor(props) {
super(props);
this.video = [];
this.state = {
playing: [false, false, false, false],
videoList: [
{
src: "https://clips.vorwaerts-gmbh.de/VfE_html5.mp4",
type: "video/mp4"
},
{
src: "https://clips.vorwaerts-gmbh.de/VfE_html5.mp4",
type: "video/mp4"
},
{
src: "https://clips.vorwaerts-gmbh.de/VfE_html5.mp4",
type: "video/mp4"
},
{
src: "https://clips.vorwaerts-gmbh.de/VfE_html5.mp4",
type: "video/mp4"
}
]
};
}
onPlayPauseClick = index => event => {
this.setState(state => {
state.playing = !state.playing;
state.playing ? this.video[index].play() : this.video[index].pause();
return state.playing[index];
});
};
onFullScreenClick = index => event => {
let video = this.video[index];
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.webkitRequestFullscreen) {
video.webkitRequestFullscreen();
} else if (video.mozRequestFullscreen) {
video.mozRequestFullscreen();
} else if (video.msRequestFullscreen) {
video.msRequestFullscreen();
}
};
renderList = () => {
const { playing } = this.state;
return this.state.videoList.map((item, index) => {
return (
<li key={`item_${index}`}>
<video
ref={video => {
this.video[index] = video;
}}
src={item.src}
/>
<img
src={
playing
? "https://icon2.kisspng.com/20180419/pyq/kisspng-computer-icons-arrow-triangle-play-icon-5ad83452103159.1624767815241186100663.jpg"
: "https://cdn2.iconfinder.com/data/icons/flat-and-simple-pack-2/512/1_Control_pause-512.png"
}
className="play"
onClick={this.onPlayPauseClick(index)}
/>
<img
src="https://cdn3.iconfinder.com/data/icons/google-material-design-icons/48/ic_fullscreen_exit_48px-512.png"
className="full"
onClick={this.onFullScreenClick(index)}
/>
</li>
);
});
};
render() {
return (
<div>
<ul>{this.renderList()}</ul>
</div>
);
}
}
export default Video;

Im use react-player, where option "controls" - Set to true or false to display native player controls. look at the "react-player", it has everything you need
<ReactPlayer url={'url/to/video'} className={classes.styleView} controls/>

Related

change text of a specific button when clicked in React

I want to change the text of a specific button when I click on that button in React. But the issue is when I click the button the title will change for all buttons!
class Results extends Component {
constructor() {
super();
this.state = {
title: "Add to watchlist"
}
}
changeTitle = () => {
this.setState({ title: "Added" });
};
render() {
return (
<div className='results'>
{
this.props.movies.map((movie, index) => {
return (
<div className='card wrapper' key={index}>
<button className='watchListButton' onClick={this.changeTitle}>{this.state.title}</button>
</div>
)
})
}
</div>
)
}
}
You would need to come up with a mechanism to track added/removed titles per movie. For that, you would have to set your state properly. Example:
this.state = {
movies: [
{id: 1, title: 'Casino', added: false},
{id: 2, title: 'Goodfellas', added: false}
]
This way you can track what's added and what's not by passing the movie id to the function that marks movies as Added/Removed. I have put together this basic Sandbox for you to get you going in the right direction:
https://codesandbox.io/s/keen-moon-9dct9?file=/src/App.js
And here is the code for future reference:
import React, { Component } from "react";
import "./styles.css";
class App extends Component {
constructor() {
super();
this.state = {
movies: [
{ id: 1, title: "Casino", added: false },
{ id: 2, title: "Goodfellas", added: false }
]
};
}
changeTitle = (id) => {
this.setState(
this.state.movies.map((item) => {
if (item.id === id) item.added = !item.added;
return item;
})
);
};
render() {
const { movies } = this.state;
return (
<div className="results">
{movies.map((movie, index) => {
return (
<div className="card wrapper" key={index}>
{movie.title}
<button
className="watchListButton"
onClick={() => this.changeTitle(movie.id)}
>
{movie.added ? "Remove" : "Add"}
</button>
</div>
);
})}
</div>
);
}
}
export default App;

React Multi Carousel - Problem with customButtonGroup

I've been struggling with what seems like a simple solution for far too long. I'm new to typescript and new to react.
I'm trying to use the react-mulit-carousel NPM package.
I'm able to get the customButtonGroup to work successfully in the sandbox:
https://codesandbox.io/s/fervent-rain-332mn?file=/src/App.js:834-913
But when I try to implement that in my SPFX solution i get the following error:
Type '{}' is missing the following properties from type '{ [x: string]: any; next: any; previous: any; goToSlide: any; }': next, previous, goToSlide
import * as React from 'react';
import { IBrandsCarouselProps } from './IBrandsCarouselProps';
import { IBrandsCarouselState } from './IBrandsCarouselState';
import { IBrand } from './IBrand';
import styles from '../../../styles/styles.module.scss';
import { SPHttpClient } from '#microsoft/sp-http';
import Carousel from 'react-multi-carousel';
import 'react-multi-carousel/lib/styles.css';
import '../../../styles/react-carousel.scss';
import { getNextElement } from 'office-ui-fabric-react';
const responsive = {
desktop: {
breakpoint: { max: 4000, min: 768 },
items: 4,
partialVisibilityGutter: 0
},
tablet: {
breakpoint: { max: 768, min: 480 },
items: 3,
partialVisibilityGutter: 30
},
mobile: {
breakpoint: { max: 480, min: 0 },
items: 2,
partialVisibilityGutter: 30
}
};
export default class BrandsCarousel extends React.Component<IBrandsCarouselProps, IBrandsCarouselState>{
constructor(props) {
super(props);
this.state = {
displayedBrands: [],
isLoading: true
};
}
/**
* Loads data from a list by using a cached view
*/
public loadBrandsFromList(): Promise<IBrand[]> {
const queryUrlGetAllItems: string = `[[HIDDEN]]`;
return this.props.context.spHttpClient.get(
queryUrlGetAllItems,
SPHttpClient.configurations.v1)
.then(
(response: any) => {
if (response.status >= 200 && response.status < 300) {
return response.json();
} else {
return Promise.resolve(new Error(JSON.stringify(response)));
}
})
.then((data: any) => {
let documents: IBrand[] = [];
if (data) {
for (let i = 0; i < data.value.length; i++) {
let item = data.value[i];
var doc: IBrand = {
Title: item.Title,
Image: item.Image.Url ? item.Image.Url : "No Image Set",
Url: item.Url.Url,
Business: item.Business
};
documents.push(doc);
}
}
return documents;
}).catch((ex) => {
// console.log("readDocumentsFromLibrary > spHttpClient.get()...catch:", ex);
throw ex;
});
}
public render(): React.ReactElement<IBrandsCarouselProps> {
// Sorting is Done in the Rest Call
let items = this.state.displayedBrands;
// create a new list that filters by the tags
// Business is an array of strings
// If the item has an array value that matches the Props Business
if (this.props.Business != "All") {
let filteredItems = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.Business.indexOf(this.props.Business) > -1) {
filteredItems.push(item);
}
}
items = filteredItems;
}
const ButtonGroup = ({ next, previous, goToSlide, ...rest }) => {
const {
carouselState: { currentSlide }
} = rest;
return (
<div className="carousel-button-group">
<div
className={currentSlide === 0 ? "disable" : ""}
onClick={() => previous()}
>
Prev
</div>
<div onClick={() => next()}>Next</div>
<div onClick={() => goToSlide(currentSlide + 1)}> Go to any slide </div>
</div>
);
};
return (
<div className={styles["brands-slider"] + " " + styles["card-docs-slider"] + " hub-carousel"}>
{this.props.IsTitle && this.props.Title != "" &&
<div className={styles["widget-header"]}>
<span className={styles["view"]}>{this.props.Title}</span>
</div>
}
<div className={styles["card-slider"]}>
{items && items.length > 0 &&
<Carousel
responsive={responsive}
arrows
additionalTransfrom={0}
itemClass={"react-carousel-item"}
minimumTouchDrag={80}
partialVisible
renderButtonGroupOutside
customButtonGroup={<ButtonGroup />}
>
{items.map((item) => {
return (
<a href={item.Url} className={styles["block-link"]} target="_blank">
<img src={item.Image} alt={item.Title} />
</a>
);
})
}
</Carousel>
}
{items && items.length == 0 &&
<p>No Brands found. Please, check the List</p>
}
</div>
</div>
);
}
public componentDidMount() {
this.loadBrandsFromList().then(
//resolve
(documents: IBrand[]) => {
this.setState({
displayedBrands: documents,
isLoading: false,
});
},
//reject
(data: any) => {
this.setState({
displayedBrands: [],
isLoading: false,
isErrorOccured: true,
errorMessage: data
});
}
).catch((ex) => {
this.setState({
displayedBrands: [],
isLoading: false,
isErrorOccured: true,
errorMessage: ex.errorMessage
});
});
}
}
Any help would be greatly appreciated. Thank you!
I was able to figure it out. I needed to pass parameters. Oops!
Hopefully this can help out another JSX, Typescript, React beginner in the future.
<Carousel
responsive={responsive}
arrows
additionalTransfrom={0}
itemClass={"react-carousel-item"}
minimumTouchDrag={80}
partialVisible
renderButtonGroupOutside
customButtonGroup={<ButtonGroup
next={this.props.next}
previous={this.props.previous}
rest={this.props.rest}
/>}
>
Here's the Custom Button group if it helps as well. I couldn't find the documentation to hide the next button.
const ButtonGroup = ({ next, previous, ...rest }) => {
const {
carouselState: { currentSlide, totalItems, slidesToShow }
} = rest;
return (
<div className="carousel-button-group">
<button aria-label="Go to previous slide"
className={currentSlide === 0 ? "disable" : "react-multiple-carousel__arrow react-multiple-carousel__arrow--left"}
onClick={() => previous()}></button>
<button aria-label="Go to next slide"
className={currentSlide === totalItems - slidesToShow ? "disable" : "react-multiple-carousel__arrow react-multiple-carousel__arrow--right"}
onClick={() => next()}></button>
</div>
);
};

How to add right click menu to react table row, and access its properties?

I've added react-table package to my project and everything is fine, but I also wanted to have a possibility to right click on a row and perform some actions on it (cancel, pause etc). I'm using React with Typescript but I hope it doesn't add any complexity.
My initial idea was to use react-contextify, however I can't find any working examples that would combine react-table and react-contextify together.
The only "working" example I have found is this one:
React Context Menu on react table using react-contexify
I ended up not using react-contextify and it "kind of works" but I'm not totally certain about this one as I sometimes keep getting exceptions like this:
Uncaught TypeError: Cannot read property 'original' of undefined
The code I have now is this:
const columns = [
{
Header: "Name",
accessor: "name"
},
{
Header: "Age",
accessor: "age",
Cell: (props: { value: React.ReactNode }) => (
<span className="number">{props.value}</span>
)
},
{
id: "friendName", // Required because our accessor is not a string
Header: "Friend Name",
accessor: (d: { friend: { name: any } }) => d.friend.name // Custom value accessors!
},
{
Header: (props: any) => <span>Friend Age</span>, // Custom header components!
accessor: "friend.age"
}
];
return (
<div>
<ContextMenuTrigger id="menu_id">
<ReactTable
data={data}
columns={columns}
showPagination={false}
getTdProps={(
state: any,
rowInfo: any,
column: any,
instance: any
) => {
return {
onClick: (e: any, handleOriginal: any) => {
const activeItem = rowInfo.original;
console.log(activeItem);
},
onContextMenu: () => {
console.log("contextMenu", rowInfo);
this.setState({
showContextMenu: true,
rowClickedData: rowInfo.original
});
}
};
}}
/>
</ContextMenuTrigger>
{this.state.showContextMenu ? (
<MyAwesomeMenu clickedData={this.state.rowClickedData} />
) : null}
</div>
);
}
}
const MyAwesomeMenu = (props: { clickedData: any }) => (
<ContextMenu id="menu_id">
<MenuItem
data={props.clickedData}
onClick={(e, props) => onClick({ e, props })}
>
<div className="green">ContextMenu Item 1 - {props.clickedData.id}</div>
</MenuItem>
</ContextMenu>
);
const onClick = (props: {
e:
| React.TouchEvent<HTMLDivElement>
| React.MouseEvent<HTMLDivElement, MouseEvent>;
props: Object;
}) => console.log("-------------->", props);
What is the best (and simplest) way to add a context menu to react-table so I can use clicked row's props? I really like react-contextify but haven't found any examples.
Thanks
React Hooks exmaple on dev.to
Class Based Compnent example on codepen
class App extends React.Component {
constructor() {
super();
this.state = {
value: ''
};
}
render() {
return(
<div>
{
['row1', 'row2', 'row3'].map((row) => {
return (
<ContextMenu
key={row}
buttons={[
{ label: 'Editovat', onClick: (e) => alert(`Editace ${row}`) },
{ label: 'Smazat', onClick: (e) => alert(`Mažu ${row}`) }
]}
>
<div className="row">{row}</div>
</ContextMenu>
);
})
}
</div>
);
}
}
class ContextMenu extends React.Component {
static defaultProps = {
buttons: []
};
constructor() {
super();
this.state = {
open: false
};
}
componentDidMount() {
document.addEventListener('click', this.handleClickOutside);
document.addEventListener('contextmenu', this.handleRightClickOutside);
}
handleClickOutside = (e) => {
if (!this.state.open) {
return;
}
const root = ReactDOM.findDOMNode(this.div);
const context = ReactDOM.findDOMNode(this.context);
const isInRow = (!root.contains(e.target) || root.contains(e.target));
const isInContext = !context.contains(e.target);
if (isInRow && isInContext) {
this.setState({
open: false
});
}
}
handleRightClickOutside = (e) => {
if (!this.state.open) {
return;
}
const root = ReactDOM.findDOMNode(this.div);
const isInRow = !root.contains(e.target);
if (isInRow) {
this.setState({
open: false
});
}
}
handleRightClick = (e) => {
e.preventDefault();
console.log(e.nativeEvent, window.scrollY);
this.setState({
open: true,
top: window.scrollY + e.nativeEvent.clientY,
left: e.nativeEvent.clientX,
});
}
render() {
return (
<div
onContextMenu={this.handleRightClick}
ref={(node) => this.div = node}
>
{this.props.children}
{
!this.state.open
? null
: <div
className="context"
ref={(div) => this.context = div}
style={{ top: this.state.top, left: this.state.left }}
>
<ul>
{
// button - name, onClick, label
this.props.buttons.length > 0 &&
this.props.buttons.map((button) => {
return <li key={button.label}>
<a href="#" onClick={button.onClick}>
{button.label}
</a>
</li>
})
}
</ul>
</div>
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));

Reactjs: How to properly fetch each users record from database on pop button click using Reactjs

The code below shows each user info on users list button click.
Now I want fetch each users record from database on users list button click.
In the open() function, I have implemented the code below
open = (id,name) => {
alert(id);
alert(name);
//start axios api call
const user_data = {
uid: 'id',
uname: 'name'
};
this.setState({ loading_image: true }, () => {
axios.post("http://localhost/data.php", { user_data })
.then(response => {
this.setState({
chatData1: response.data[0].id,
chatData: response.data,
loading_image: false
});
console.log(this.state.chatData);
alert(this.state.chatData1);
})
.catch(error => {
console.log(error);
});
});
}
In class OpenedUser(), I have initialize in the constructor the code below
chatData: []
In the render method have implemented the code
<b> Load Message from Database for each user ({this.state.chatData1})</b>
<div>
{this.state.chatData.map((pere, i) => (<li key={i}>{pere.lastname} - {pere.id}----- {pere.username}</li>))}
</div>
Here is my Issue:
My problem is that the Axios Api is getting the result but am not seeing any result in the render method.
but I can see it in the console as per code below
Array(1)
0: {id: "1", firstname: "faco", lastname: "facoyo"}
length: 1
Here is an example of json api response.
[{"id":"1","firstname":"faco","lastname":"facoyo"}]
Here is the full code
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import { Link } from 'react-router-dom';
import axios from 'axios';
class User extends React.Component {
open = () => this.props.open(this.props.data.id, this.props.data.name);
render() {
return (
<React.Fragment>
<div key={this.props.data.id}>
<button onClick={() => this.open(this.props.data.id,this.props.data.name)}>{this.props.data.name}</button>
</div>
</React.Fragment>
);
}
}
class OpenedUser extends React.Component {
constructor(props) {
super(props);
this.state = {
chatData: [],
hidden: false,
};
}
componentDidMount(){
} // close component didmount
toggleHidden = () =>
this.setState(prevState => ({ hidden: !prevState.hidden }));
close = () => this.props.close(this.props.data.id);
render() {
return (
<div key={this.props.data.id} style={{ display: "inline-block" }}>
<div className="msg_head">
<button onClick={this.close}>close</button>
<div>user {this.props.data.id}</div>
<div>name {this.props.data.name}</div>
{this.state.hidden ? null : (
<div className="msg_wrap">
<div className="msg_body">Message will appear here</div>
<b> Load Message from Database for each user ({this.state.chatData1}) </b>
<div>
{this.state.chatData.map((pere, i) => (
<li key={i}>
{pere.lastname} - {pere.id}----- {pere.username}
</li>
))}
</div>
</div>
)}
</div>
</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
shown: true,
activeIds: [],
data: [
{ id: 1, name: "user 1" },
{ id: 2, name: "user 2" },
{ id: 3, name: "user 3" },
{ id: 4, name: "user 4" },
{ id: 5, name: "user 5" }
],
};
}
toggle() {
this.setState({
shown: !this.state.shown
});
}
open = (id,name) => {
alert(id);
alert(name);
//start axios api call
const user_data = {
uid: 'id',
uname: 'name'
};
this.setState({ loading_image: true }, () => {
axios.post("http://localhost/apidb_react/search_data.php", { user_data })
.then(response => {
this.setState({
chatData1: response.data[0].id,
chatData: response.data,
loading_image: false
});
console.log(this.state.chatData);
alert(this.state.chatData1);
})
.catch(error => {
console.log(error);
});
});
// end axios api call
this.setState((prevState) => ({
activeIds: prevState.activeIds.find((user) => user === id)
? prevState.activeIds
: [...prevState.activeIds, id]
}));
}
close = id => {
this.setState((prevState) => ({
activeIds: prevState.activeIds.filter((user) => user !== id),
}));
};
renderUser = (id) => {
const user = this.state.data.find((user) => user.id === id);
if (!user) {
return null;
}
return (
<OpenedUser key={user.id} data={user} close={this.close}/>
)
}
renderActiveUser = () => {
return (
<div style={{ position: "fixed", bottom: 0, right: 0 }}>
{this.state.activeIds.map((id) => this.renderUser(id)) }
</div>
);
};
render() {
return (
<div>
{this.state.data.map(person => (
<User key={person.id} data={person} open={this.open} />
))}
{this.state.activeIds.length !== 0 && this.renderActiveUser()}
</div>
);
}
}
The problem is you're making the request in the App component and storing in state but you're trying to access the state in a child component so it will never actually read the data.
To fix this you need to pass in the chat data via prop
<OpenedUser
chatData={this.state.chatData}
key={user.id}
data={user}
close={this.close}
/>
Note: In my runnable example, I've replaced your api endpoint with a mock api promise.
const mockApi = () => {
return new Promise((resolve, reject) => {
const json = [{ id: "1", firstname: "faco", lastname: "facoyo" }];
resolve(json);
});
};
class User extends React.Component {
open = () => this.props.open(this.props.data.id, this.props.data.name);
render() {
return (
<React.Fragment>
<div key={this.props.data.id}>
<button
onClick={() => this.open(this.props.data.id, this.props.data.name)}
>
{this.props.data.name}
</button>
</div>
</React.Fragment>
);
}
}
class OpenedUser extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: false
};
}
componentDidMount() {} // close component didmount
toggleHidden = () =>
this.setState(prevState => ({ hidden: !prevState.hidden }));
close = () => this.props.close(this.props.data.id);
render() {
return (
<div key={this.props.data.id} style={{ display: "inline-block" }}>
<div className="msg_head">
<button onClick={this.close}>close</button>
<div>user {this.props.data.id}</div>
<div>name {this.props.data.name}</div>
{this.state.hidden ? null : (
<div className="msg_wrap">
<div className="msg_body">Message will appear here</div>
<b>
{" "}
Load Message from Database for each user ({this.state.chatData1}
){" "}
</b>
<ul>
{this.props.chatData.map((pere, i) => (
<li key={i}>
{pere.lastname} - {pere.id}----- {pere.username}
</li>
))}
</ul>
</div>
)}
</div>
</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
shown: true,
chatData: [],
activeIds: [],
data: [
{ id: 1, name: "user 1" },
{ id: 2, name: "user 2" },
{ id: 3, name: "user 3" },
{ id: 4, name: "user 4" },
{ id: 5, name: "user 5" }
]
};
}
toggle() {
this.setState({
shown: !this.state.shown
});
}
open = (id, name) => {
alert(id);
alert(name);
//start axios api call
const user_data = {
uid: "id",
uname: "name"
};
// this.setState({ loading_image: true }, () => {
// axios
// .post("http://localhost/apidb_react/search_data.php", { user_data })
// .then(response => {
// this.setState({
// chatData1: response.data[0].id,
// chatData: response.data,
// loading_image: false
// });
// console.log(this.state.chatData);
// alert(this.state.chatData1);
// })
// .catch(error => {
// console.log(error);
// });
// });
this.setState({ loading_image: true }, () => {
mockApi().then(data => {
this.setState({
chatData1: data[0].id,
chatData: data,
loading_image: false
});
});
});
// end axios api call
this.setState(prevState => ({
activeIds: prevState.activeIds.find(user => user === id)
? prevState.activeIds
: [...prevState.activeIds, id]
}));
};
close = id => {
this.setState(prevState => ({
activeIds: prevState.activeIds.filter(user => user !== id)
}));
};
renderUser = id => {
const user = this.state.data.find(user => user.id === id);
if (!user) {
return null;
}
return (
<OpenedUser
chatData={this.state.chatData}
key={user.id}
data={user}
close={this.close}
/>
);
};
renderActiveUser = () => {
return (
<div style={{ position: "fixed", bottom: 0, right: 0 }}>
{this.state.activeIds.map(id => this.renderUser(id))}
</div>
);
};
render() {
return (
<div>
{this.state.data.map(person => (
<User key={person.id} data={person} open={this.open} />
))}
{this.state.activeIds.length !== 0 && this.renderActiveUser()}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<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>
<div id="root"></div>
I see a few missing points in your code namely you are using li without ul which is a kind of invalid markup, then you have mapping for .username which is undefined field according to response which may also throw error.

react-photo-gallery don't change photos via state

The photo object is not changing in the Gallery component via state.
I created a "folder gallery", a multi gallery component to render a new one if you select it by click.
See the demonstration:
https://codesandbox.io/s/50wr02n8q4
github issue:
https://github.com/neptunian/react-photo-gallery/issues/118
Envs:
react-photo-gallery: 6.2.2
react: 16.6.3
npm: 6.4.1
See the code below:
const folderPhotos = [
{
title: "Gallery one",
photos: [
{
src: "https://source.unsplash.com/2ShvY8Lf6l0/800x599"
},
{
src: "https://source.unsplash.com/Dm-qxdynoEc/800x799"
},
{
src: "https://source.unsplash.com/qDkso9nvCg0/600x799"
}
]
},
{
title: "Gallery two",
photos: [
{
src: "https://source.unsplash.com/iecJiKe_RNg/600x799"
},
{
src: "https://source.unsplash.com/epcsn8Ed8kY/600x799"
},
{
src: "https://source.unsplash.com/NQSWvyVRIJk/800x599"
}
]
}
];
The photoProps dimensions object:
const photoProps = {
width: 1,
height: 1
};
The internal component here:
<Gallery
columnsLength={6}
photosObj={folderPhotos}
photoProps={photoProps}
withLightbox={true}
/>
Now the Gallery component:
import _ from "lodash";
import React, { Component, Fragment } from "react";
import Gallery from "react-photo-gallery";
import Lightbox from "react-images";
export class PhotoGallery extends Component {
constructor(props) {
super(props);
// Bindables
this.showGallery = this.showGallery.bind(this);
this.openLightbox = this.openLightbox.bind(this);
this.closeLightbox = this.closeLightbox.bind(this);
this.goToPrevious = this.goToPrevious.bind(this);
this.goToNext = this.goToNext.bind(this);
}
state = {
photosObj: [],
currentImage: 0,
lightboxIsOpen: false
};
async showGallery(itemObj, photoProps) {
let photosObj = [];
if (!_.isEmpty(itemObj)) {
photosObj = await Object.keys(itemObj)
.map(i => itemObj[i])
.map(item => ({
...item,
width: photoProps.width,
height: photoProps.height
}));
this.setState({
photosObj
});
console.log("-- props: ", this.state.photosObj);
}
}
openLightbox(event, obj) {
this.setState({
currentImage: obj.index,
lightboxIsOpen: true
});
}
closeLightbox() {
this.setState({
currentImage: 0,
lightboxIsOpen: false
});
}
goToPrevious() {
this.setState({
currentImage: this.state.currentImage - 1
});
}
goToNext() {
this.setState({
currentImage: this.state.currentImage + 1
});
}
render() {
const { columnsLength, photosObj, photoProps, withLightbox } = this.props;
return (
<div className="section-body">
<div className="content-col-info">
<ul className="list-mentors w-list-unstyled">
{photosObj.map((itemObj, i) => (
<li key={i}>
<span
className="mentors-item w-inline-block"
onClick={() => this.showGallery(itemObj.photos, photoProps)}
>
<div>{itemObj.title}</div>
</span>
</li>
))}
</ul>
</div>
<div className="content-col-info">
{!_.isEmpty(this.state.photosObj) && (
<Gallery
columns={columnsLength}
photos={this.state.photosObj}
onClick={withLightbox ? this.openLightbox : null}
/>
)}
{!_.isEmpty(this.state.photosObj) && withLightbox && (
<Lightbox
images={this.state.photosObj}
onClose={this.closeLightbox}
onClickPrev={this.goToPrevious}
onClickNext={this.goToNext}
currentImage={this.state.currentImage}
isOpen={this.state.lightboxIsOpen}
/>
)}
</div>
</div>
);
}
}
export default PhotoGallery;
EDIT - I updated the Gallery props
I fix the Gallery component props of the example too.
loadGallery(columnsLength) {
import("./photo-gallery").then(Gallery => {
console.log("-- load gallery: ", this.state.photosObj);
return (
<Gallery.default
columnsLength={columnsLength}
photosObj={this.state.photosObj}
withLightbox={true}
/>
);
});
}
And to call this:
{!_.isEmpty(this.state.photosObj) && this.loadGallery(columnsLength)}
References:
Intro to Dynamic import() in Create React App
React images
Since photos option is required in Gallery.js (Library)
Gallery.propTypes = {
photos: _propTypes2.default.arrayOf(_Photo.photoPropType).isRequired,
direction: _propTypes2.default.string,
onClick: _propTypes2.default.func,
columns: _propTypes2.default.number,
margin: _propTypes2.default.number,
ImageComponent: _propTypes2.default.func
};
Pass "photos={this.state.photosObj}" in <Gallery /> of Gallery.js (your js file) as follows:
Code:
<Gallery
columnsLength={columnsLength}
photosObj={this.state.photosObj}
photos={this.state.photosObj}
withLightbox={true}
/>
I am not sure why you pass other options.

Resources