React Current Image in Image Gallery - reactjs

Hello i have a litle problem with react-image-gallery.
In ImageGallery component i pass startIndex value like this.state.currentImage and this.state.currentImage depends on number photo with we are going to click.
When we click on photo for example number 4 this.state.currentImage is chaining on number 4 and its correct for me but in <imageGallery/> component startIndex doesn't work like i should. My modal always start on first image index[0].
import React, { Component } from "react";
import { Modal, ModalClose, ModalBody } from "react-modal-bootstrap";
import ImageGallery from "react-image-gallery";
import "./index.css";
export default class Images extends Component {
constructor(props) {
super(props);
var data = { title: "photos", images: [], ...props.data };
this.state = {
open: false,
showPlayButton: true,
showGalleryPlayButton: false,
showFullscreenButton: true,
showGalleryFullscreenButton: false,
currentImage: 0,
test: 0,
player: [],
data: data
};
console.log("Images: ", this.state.data);
this.openLightbox = this.openLightbox.bind(this);
this._renderImages = this._renderImages.bind(this);
this._onSlide = this._onSlide.bind(this);
this._onReady = this._onReady.bind(this);
}
state = {
isOpen: false
};
openModal = event => {
console.log(event.target);
this.setState({ isOpen: true });
};
openLightbox(index, event) {
// console.log('index',index);
event.preventDefault();
// this.setState({
// isOpen: true,
// currentImage: index
// });
this.setState(
prevState => {
return {
currentImage: index,
isOpen: true
};
},
() => {
console.log("currentImage", this.state.currentImage);
console.log("event", index);
}
);
}
hideModal = () => {
this.setState({ isOpen: false });
};
_renderImages(item) {
return (
<div className="images image-gallery-image">
<div className="images image-wrapper">
<h1>{this.state.currentImage}</h1>
<img src={item.img} alt="" className="images multimedia_image" />
<span className="images image-gallery-description">{item.desc}</span>
</div>
</div>
);
}
_onReady(event) {
const player = this.state.player;
player.push(event.target);
this.setState({
player: player
});
}
_onSlide() {
this.state.data.images.forEach(player => {});
}
handleImageLoad(event) {
console.log("Image loaded ", event.target);
}
render() {
var openLightbox = this.openLightbox;
var currentImage = this.state.currentImage;
const number = this.state.currentImage;
return (
<div className="images row">
<div className="images col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div className="images title">{this.state.data.title}</div>
</div>
<div className="images col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div className="images row">
{this.state.data.images.map((object, i) => {
let backgroundImage = {
backgroundImage: "url(" + object.thumbnail + ")",
top: 0,
right: 0,
left: 0,
bottom: 0,
position: "absolute",
flex: 1,
backgroundPosition: "center",
backgroundSize: "cover",
zIndex: 1
};
return (
<div
className="images item col-xs-4 col-sm-4 col-md-3 col-lg-3 images__single-item"
key={i}
>
<div
className="images multimedia_button"
onClick={e => this.openLightbox(i, e)}
>
<div style={backgroundImage} />
</div>
</div>
);
})}
</div>
</div>
<Modal isOpen={this.state.isOpen} onRequestHide={this.hideModal}>
<button
type="button"
className="images player_button_close"
onClick={this.hideModal}
>
X
</button>
<ModalBody>
<ImageGallery
items={this.state.data.images}
startIndex={this.state.currentImage}
slideInterval={2000}
showPlayButton={false}
showFullscreenButton={false}
onImageLoad={this.handleImageLoad}
onSlide={this._onSlide}
showIndex={true}
renderItem={this._renderImages}
/>
</ModalBody>
</Modal>
</div>
);
}
}

I found a solution when I needed to reinitialize current element. It can be useful for some other dev.
The answer is A “key” a special string attribute when we create lists of elements.
Whenever "key" is going change element is rerender. So
this.state = {
open: false,
showPlayButton: true,
showGalleryPlayButton: false,
showFullscreenButton: true,
showGalleryFullscreenButton: false,
currentImage: 0,
test: 0,
player: [],
data: data,
number:0
};
openLightbox(index, event) {
event.preventDefault();
this.setState(
prevState => {
return {
currentImage: index,
isOpen: true,
number:prevState.number+1
};
},
() => {
console.log("currentImage", this.state.currentImage);
console.log("event", index);
}
);
And here we neet to add our key={this.state.number}
<Modal isOpen={this.state.isOpen} onRequestHide={this.hideModal}>
<button
type="button"
className="images player_button_close"
onClick={this.hideModal}
>
X
</button>
<ModalBody>
<ImageGallery
key={this.state.number}
items={this.state.data.images}
startIndex={this.state.currentImage}
slideInterval={2000}
showPlayButton={false}
showFullscreenButton={false}
onImageLoad={this.handleImageLoad}
onSlide={this._onSlide}
showIndex={true}
renderItem={this._renderImages}
/>
</ModalBody>
</Modal>

Related

Toggle icon with error in reactjs. How to solve?

By clicking on the heart icon I want to change the color to red and add it to the favorite. When clicking again, it goes back to gray and remove from favorite. But when I click on an icon they all change color. How to fix to change only the icon that was clicked?
const Destaques = () => {
const { destaques, favoriteList, setFavoriteList, isFavorite, setIsFavorite, handlerIcon } = useContext(MoviesContext);
const [showStatus, setShowStatus] = useState(false);
const handleShow = () => setShowStatus(true);
const handleClose = () => setShowStatus(false);
var settings = {
infinite: false,
autoplay: true,
speed: 500,
slidesToShow: 4,
infinite: true,
slidesToScroll: 1,
initialSlide: 0,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 1,
infinite: true,
}
},
{
breakpoint: 600,
settings: {
slidesToShow: 2,
slidesToScroll: 1,
initialSlide: 2
}
},
{
breakpoint: 480,
settings: {
slidesToShow: 1,
slidesToScroll: 1
}
}
]
};
const filteredHighlight = destaques.filter(movie => movie.highlight === true);
const sliders = () => {
return filteredHighlight.map((data) => {
return (
<div>
<div className='container-card' key={data.id}>
<div className='card-img'>
<img alt={data.title} src={data.poster} onClick={handleShow} />
<i onClick={handlerIcon}>
{isFavorite ?
<FaHeart className='heartIcon' style={{ color: 'red' }} /> : <FaHeart className='heartIcon' style={{ color: '#BABABA' }} />
}
</i>
</div>
<div className='box-content'>
<div className='box-title-vote'>
<h4 className='card-title'>
{data.title}
</h4>
<div className='box-vote-like'>
<span className='card-vote-average'>4/5</span>
<i className='icon-like-destaques'>
<img src={IconLike} alt="icon like"></img>
</i>
</div>
</div>
<p className='card-overview'>
{data.overview}
</p>
</div>
</div>
<Modal show={showStatus} onHide={handleClose} backdrop="static" centered >
<Modal.Header closeButton>
</Modal.Header>
<Modal.Body >
<DefaultDetail id={data.id} poster={data.poster} overview={data.overview} title={data.title} />
</Modal.Body>
</Modal>
</div>
);
});
}
return (
<div>
<h3 className='title-destaque'>Destaques</h3>
<Slider {...settings}>
{sliders()}
</Slider>
</div>
);
}
export default Destaques
Before clicking the icon
After clicking the icon
Code to add, remove and toggle icon favorite.
const MoviesContextProvider = props => {
const results = dataAllMovies.movies;
const [destaques, setDestaques] = useState(results);
const [allMovies, setAllMovies] = useState(results);
const [isFavorite, setIsFavorite] = useState(false);
const [favoriteList, setFavoriteList] = useState([]);
//favorite
const getMovieStorage = () => {
if (localStorage.getItem("favorites")) {
let favoriteList = JSON.parse(localStorage.getItem("favorites"));
return favoriteList
}
else {
let favoriteList = []
return favoriteList
}
}
useEffect(() => {
if (favoriteList.includes(allMovies.id)) {
setIsFavorite(!isFavorite);
const favorite = getMovieStorage();
setFavoriteList(favorite);
}
}, []);
const handlerIcon = (e) => {
setIsFavorite(!isFavorite);
if (isFavorite) {
var index = favoriteList.indexOf(allMovies.id);
favoriteList.splice(index, 1);
setFavoriteList(favoriteList);
deleteMovie(allMovies.id);
}
else {
setFavoriteList(favoriteList.concat([allMovies.id]));
addMovie(allMovies.id);
}
}
const deleteMovie = (id) => {
const Favorite = getMovieStorage();
var index = Favorite.indexOf(id)
Favorite.splice(index, 1);
localStorage.setItem("FavoritList", JSON.stringify(Favorite));
}
const addMovie = (id) => {
const Favorite = getMovieStorage();
Favorite.push(id)
localStorage.setItem("FavoritList", JSON.stringify(Favorite))
}
return (
<MoviesContext.Provider value={{ allMovies, destaques, filterMovies, filteredMovies, isFavorite, setIsFavorite, addFavoriteMovie, removeFavoriteMovie, handleFavClick, handlerIcon, favoriteList, getMovieStorage }}>
{props.children}
</MoviesContext.Provider>
);
}
export default MoviesContextProvider
PLEASE HELP ME. I NEED TO SOLVE THIS PROBLEM.
You must have an array of movies ids and check it with each movie id, now you check isFavorite, is not true
change isFavorite condition to => favoriteList.includes(data.id)
and fill favoriteList with movies ids in your context
<div className='card-img'>
<img alt={data.title} src={data.poster} onClick={handleShow} />
<i onClick={handlerIcon}>
{favoriteList.includes(data.id) ?
<FaHeart className='heartIcon' style={{ color: 'red' }} /> : <FaHeart className='heartIcon' style={{ color: '#BABABA' }} />
}
</i>
</div>

React Modal with axios infinite loop

The print statement in my if (this.props.currentNode.getAttribute("label").toLowerCase() === "data")
is being called infinite times which keeps on printing in modal and sending a post request until the modal is closed.
This does not happen when I put the post call in ComponentDidMount
Can anyone please explain why this is happening and what I can do to avoid this?
class ConfigurationModal extends React.Component {
constructor(props) {
super(props);
this.state = {
selectSource: [],
};
}
el = document.createElement("div");
componentDidMount() {
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
jsonIsEmpty = (obj) => {
return obj === "[object Object]" ? true : false;
};
render() {
// The gray background
const backdropStyle = {
position: "fixed",
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: "rgba(0,0,0,0.3)",
padding: 50,
};
if (this.props.currentNode.getAttribute("label").toLowerCase() === "data") {
console.log("in modal")
http.post("configure_get_sources", {
headers: {
"content-type": "application/json",
},
})
.then((res) => {
this.setState({ selectSource: res.data });
});
var tempConfig = this.jsonIsEmpty(
this.props.currentNode.getAttribute("configuration")
)
? {}
: JSON.parse(this.props.currentNode.getAttribute("configuration"));
let renderedSources = this.state.selectSource.map((item, i) => {
return (
<option value={item} key={i} selected={item === tempConfig["Source"]}>
{item}
</option>
);
});
return ReactDOM.createPortal(
<div className="backdrop" style={backdropStyle}>
<Modal.Dialog>
<Modal.Header>
<Modal.Title>Configure Node</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="container">
<div className="row">
<label className="col-md-4">Data Source: </label>
<select className="col-md-7" id="dataSelect">
{renderedSources}
</select>
</div>
<div className="row col-md-3"></div>
</div>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.props.onClose}>
Close
</Button>
<Button variant="primary" onClick={this.props.saveModal}>
Save changes
</Button>
</Modal.Footer>
</Modal.Dialog>
</div>,
this.el
);
}
return "";
}
}
export default ConfigurationModal;
You're modifying component's state inside render, this causing an infinity loop. Because of react re-rendering component after each props or state change. https://reactjs.org/docs/faq-state.html

React for table rows to bind (this) click event handler

I have a table rows data from server containing images (some other data is removed for simplicity). When images clicked, a modal popup is shown to preview the loaded image to crop and change the image with the cropped one. Everything is work fine.
The problem is, the clicked image on the row should change after the modal submit button is clicked. But I found that the image on the last row is changed.
I know the problem comes from this line but I have no idea how to solve it :
handleSubmit = e => {
e.preventDefault();
console.log(this.state.croppedImageUrl);
this.imagetoCropt.src = this.state.croppedImageUrl;
};
This is the code :
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Button } from "react-bootstrap";
import { Modal } from "react-bootstrap";
import ReactCrop from "react-image-crop";
import "react-image-crop/dist/ReactCrop.css";
import { my_ads } from "./component/AdsFunctions";
export default class Myads extends Component {
constructor() {
super();
this.state = {
myads : {},
modalShow: false,
setShow: false,
setClose: true,
previewImage: "/assets/loader.gif",
src: null,
crop: {
unit: "%",
width: 30,
aspect: 5 / 4
}
};
}
handleImageOnChange = e => {
if (e.target.files && e.target.files.length > 0) {
const reader = new FileReader();
reader.addEventListener("load", () =>
this.setState({
src: reader.result,
modalShow: true
})
);
reader.readAsDataURL(e.target.files[0]);
}
};
onImageLoaded = image => {
this.imageRef = image;
};
onCropComplete = crop => {
this.makeClientCrop(crop);
};
onCropChange = (crop, percentCrop) => {
this.setState({ crop });
};
async makeClientCrop(crop) {
if (this.imageRef && crop.width && crop.height) {
const croppedImageUrl = await this.getCroppedImg(
this.imageRef,
crop,
"newFile.jpeg"
);
this.setState({ croppedImageUrl });
}
}
getCroppedImg(image, crop, fileName) {
const canvas = document.createElement("canvas");
const scaleX = image.naturalWidth / image.width;
const scaleY = image.naturalHeight / image.height;
canvas.width = crop.width;
canvas.height = crop.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(
image,
crop.x * scaleX,
crop.y * scaleY,
crop.width * scaleX,
crop.height * scaleY,
0,
0,
crop.width,
crop.height
);
return new Promise((resolve, reject) => {
canvas.toBlob(blob => {
if (!blob) {
//reject(new Error('Canvas is empty'));
console.error("Canvas is empty");
return;
}
blob.name = fileName;
window.URL.revokeObjectURL(this.fileUrl);
this.fileUrl = window.URL.createObjectURL(blob);
resolve(this.fileUrl);
}, "image/jpeg");
});
}
//---- modal function ------------
handleShow = () => {
this.setState({
modalShow: true
});
};
handleClose = () => {
this.setState({
modalShow: false
});
};
handleImgClick = () => {
this.refs.fileInput.click();
};
handleClickSubmit = () => {
this.refs.btnSubmit.click();
this.setState({
modalShow: false
});
};
//--------- end modal function---
//======== PROBLEM HERE ======================
handleSubmit = e => {
e.preventDefault();
console.log(this.state.croppedImageUrl);
this.imagetoCropt.src = this.state.croppedImageUrl;
};
//=============================================
componentDidMount() {
// AXIOS call
my_ads().then(res => {
this.setState({
myads: res.myads,
});
});
}
render() {
const { crop, croppedImageUrl, src } = this.state;
const show = this.state.modalShow;
// My Ads List from AXIOS call
let myads = this.state.myads;
const RenderMyAds = Object.keys(myads).map((val, index) => (
<tr className="mt-3" key={index}>
<td>
<div className="float-left mr-4">
<div className="card mb-10">
<Link to="#">
<img
className="img-thumbnail img-responsive"
src={myads[val].image}
alt="img"
width={200}
onClick={this.handleImgClick}
ref={ref => (this.imagetoCropt = ref)} <<==== problem here?
/>
</Link>
</div>
</div>
</td>
</tr>
));
return (
<div>
<section>
<div className="container">
<div className="row">
<div className="col-lg-12">
<div className="card">
<div className="card-body">
<div className="table-responsive">
<table className="table table-bordered border-top mb-0">
<tbody>
{RenderMyAds}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<form
encType="multipart/form-data"
acceptCharset="utf-8"
onSubmit={this.handleSubmit}
>
<input
type="file"
className="d-none"
name="userfile"
ref="fileInput"
onChange={this.handleImageOnChange}
/>
<button type="submit" className="d-none" ref="btnSubmit">
Upload Image
</button>
</form>
<Modal size="lg" show={show} onHide={this.handleClose}>
<Modal.Header closeButton>
<Modal.Title>Image Preview</Modal.Title>
</Modal.Header>
<Modal.Body className="text-center"></Modal.Body>
<ReactCrop
src={src}
crop={crop}
onImageLoaded={this.onImageLoaded}
onComplete={this.onCropComplete}
onChange={this.onCropChange}
/>
<img className="d-none" alt="Crop" src={croppedImageUrl} />
<Modal.Footer>
<Button
variant="primary"
className="btn-block"
onClick={this.handleClickSubmit}
>
<i className="fa fa-image mr-2"></i> Upload Image
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
You are overwriting the same ref in your map. Consequentially, the last row is the last one to be mapped. You need to instead use an array of refs.
In your contructor, add:
this.imageRefs = [];
Then in your mapping:
const RenderMyAds = Object.keys(myads).map((val, index) => (
<tr className="mt-3" key={index}>
<td>
<div className="float-left mr-4">
<div className="card mb-10">
<Link to="#">
<img
className="img-thumbnail img-responsive"
src={myads[val].image}
alt="img"
width={200}
onClick={this.handleImgClick}
ref={ref => (this.imageRefs[index] = ref)}
/>
</Link>
</div>
</div>
</td>
</tr>
));
This will let you access the correct ref, based on the key assigned to the tr.

Unexpected Behavior when Removing Items in React.js

I've started to brush up on React after a long time away. Pretty much I have a "list" that stores companies interview processes. This is built by 2 react components. Is the list that aggregates each job.
When you go to "remove row" react registers the correct "row" to delete, (and by using a debugging simple case this happens) but it will not successfully update the inner component.
I've spent time researching this, and I've added a simple component called "Welcome." This helps me because I can use this to validate that I am removing the correct element, just the inner "jobrow" component is not updating correctly.
https://codepen.io/anon/pen/XwaWPj
class Jobs extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
jobs: props.data.items
//jobs: [{ id: "" }]
};
}
handleAddJob = () => {
this.setState({
jobs: this.state.jobs.concat([{ "company":"", "position": "", "next_steps": []}])
});
console.log(this.state);
};
handleRemoveJob = key => () => {
//var index = this.state.jobs.indexOf(items)
console.log(this.state.jobs.filter((item, j) => item.key !== key) )
this.setState({
//shareholders: this.state.shareholders.filter((s, sidx) => idx !== sidx)
//next_steps: this.state.next_steps.splice(idx, 1)
jobs: this.state.jobs.filter((item, j) => item.key !== key)
});
};
//<JobRow
// company={items.company}
// position={items.position}
// next_steps={items.next_steps}/>
render() {
return (
<div>
<h4>Jobs Applied</h4>
{this.state.jobs.map((items =>
<div>
<Welcome name={items.company} />
<JobRow
company={items.company}
position={items.position}
next_steps={items.next_steps}/>
<button
type="button"
onClick={this.handleRemoveJob(items.key)} //.bind(this)
className="small">
remove row
</button>
</div>
))
}
<button
type="button"
onClick={this.handleAddJob}
className="small">
Add New Job
</button>
</div>
)
};
}
// ===========
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// ===========
//https://stackoverflow.com/questions/50147840/how-to-format-and-display-json-data-using-array-map-in-reactjs
class JobRow extends React.Component {
constructor(props) {
super(props);
this.state = {
company: props.company,
position: props.position,
next_steps: props.next_steps,
};
}
handleNameChange = evt => {
this.setState({ name: evt.target.value });
};
handleAddField = () => {
this.setState({
//shareholders: this.state.shareholders.concat([{ name: "" }])
next_steps: this.state.next_steps.concat("")
});
};
handleRemoveField = idx => () => {
this.setState({
//shareholders: this.state.shareholders.filter((s, sidx) => idx !== sidx)
//next_steps: this.state.next_steps.splice(idx, 1)
next_steps: this.state.next_steps.filter((s, sidx) => idx !== sidx)
});
};
changeTextCompany(event){
this.setState(
//"{this.state.textValue : event.target.value}"
{company: event.target.value}
);
}
render() {
return (
<div>
<div class="flex-container">
<div class="inner_flex">
<span>
<input type="text" class="form-control" placeholder="Company" value={this.state.company} id="comapny_input" onChange={this.changeTextCompany}/>
</span>
<span>
<input type="text" class="form-control" placeholder="Position" value={this.state.position} oninput="selectJobType()" id="position_input"/>
</span>
<span>
<select id="position_type">
<option value="fulltime">Fulltime</option>
<option value="intern">Co-Op/Internship</option>
</select>
</span>
</div>
{this.state.next_steps.map((step, idx) => (
<span>
<button
type="button"
onClick={this.handleRemoveField(idx)}
className="small"
>
-
</button>
<input placeholder="Next State" value={step} />
</span>
))}
<button
type="button"
onClick={this.handleAddField}
className="small">
Next Stage
</button>
</div>
</div>
);
}
}
I would like for the correct row that is removed to be reflected in the text boxes.
I'd really appreciate your feedback.
You need to give each item in an array of elements a key (you should be getting a console warning about that). And the key should be a unique identifier, NOT the array index. In your code pen, instead of
{this.state.jobs.map((items =>
<div>
try
{this.state.jobs.map((items =>
<div key={items.key}>
Then it correctly deletes row you're selecting. And research why using array indices as a key (or why not using keys at all for arrays of components) causes problems in React.
Use getDerivedStateFromProps to update state in your JobsRow.
.flex-container {
display: flex;
background-color: #f1f1f1;
}
.flex-container > div {
background-color: #B6E3DC;
margin: 0px;
padding: 5px;
}
.flex-container > div > span {
display: inline-block;
padding: 2.5px;
}
input {display: block !important; padding: 0 !important; margin: 0 !important; border: 0 !important; width: 100% !important; border-radius: 0 !important; line-height: 1 !important;}
td {margin: 0 !important; padding: 0 !important;}
input {display: block !important; padding: 0 !important; margin: 0 !important; border: 0 !important; width: 100% !important; border-radius: 0 !important; line-height: 1 !important;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
var json = {"items":[
{"key":132, "company":"Google", "position": "SE Intern", "next_steps": ["Coding", "phone"]
},
{"key":133, "company":"FaceBook", "position": "DS Intern", "next_steps": ["Onsite", "Offer"]
},
{"key":134, "company":"twitter", "position": "architectre", "next_steps": ["coffeechat", "denail"]
},
{"key":135, "company":"oracle", "position": "sleeping", "next_steps": []
}
]}
class Jobs extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
jobs: this.props.data.items
//jobs: [{ id: "" }]
};
}
handleAddJob = () => {
this.setState({
jobs: this.state.jobs.concat([
{ company: '', position: '', next_steps: [] }
])
});
console.log(this.state);
};
handleRemoveJob = key => () => {
//var index = this.state.jobs.indexOf(items)
//console.log(this.state.jobs.filter((item, j) => item.key !== key));
this.setState({
//shareholders: this.state.shareholders.filter((s, sidx) => idx !== sidx)
//next_steps: this.state.next_steps.splice(idx, 1)
jobs: this.state.jobs.filter((item, j) => item.key !== key)
});
};
//<JobRow
// company={items.company}
// position={items.position}
// next_steps={items.next_steps}/>
render() {
return (
<div>
<h4>Jobs Applied</h4>
{this.state.jobs.map(items => (
<div>
<Welcome name={items.company} />
<JobRow
company={items.company}
position={items.position}
next_steps={items.next_steps}
/>
<button
type="button"
onClick={this.handleRemoveJob(items.key)} //.bind(this)
className="small"
>
remove row
</button>
</div>
))}
<button type="button" onClick={this.handleAddJob} className="small">
Add New Job
</button>
</div>
);
}
}
// ===========
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// ===========
//https://stackoverflow.com/questions/50147840/how-to-format-and-display-json-data-using-array-map-in-reactjs
class JobRow extends React.Component {
constructor(props) {
super(props);
this.state = {
company: props.company,
position: props.position,
next_steps: props.next_steps
};
}
static getDerivedStateFromProps(props, state) {
// compare props with state data
// if they are not equal return props
// or return null
// more info here https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
return props;
}
handleNameChange = evt => {
this.setState({ name: evt.target.value });
};
handleAddField = () => {
this.setState({
//shareholders: this.state.shareholders.concat([{ name: "" }])
next_steps: this.state.next_steps.concat('')
});
};
handleRemoveField = idx => () => {
this.setState({
//shareholders: this.state.shareholders.filter((s, sidx) => idx !== sidx)
//next_steps: this.state.next_steps.splice(idx, 1)
next_steps: this.state.next_steps.filter((s, sidx) => idx !== sidx)
});
};
changeTextCompany(event) {
this.setState(
//"{this.state.textValue : event.target.value}"
{ company: event.target.value }
);
}
render() {
return (
<div>
<div class="flex-container">
<div class="inner_flex">
<span>
<input
type="text"
class="form-control"
placeholder="Company"
value={this.state.company}
id="comapny_input"
onChange={this.changeTextCompany}
/>
</span>
<span>
<input
type="text"
class="form-control"
placeholder="Position"
value={this.state.position}
oninput="selectJobType()"
id="position_input"
/>
</span>
<span>
<select id="position_type">
<option value="fulltime">Fulltime</option>
<option value="intern">Co-Op/Internship</option>
</select>
</span>
</div>
{this.state.next_steps.map((step, idx) => (
<span>
<button
type="button"
onClick={this.handleRemoveField(idx)}
className="small"
>
-
</button>
<input placeholder="Next State" value={step} />
</span>
))}
<button type="button" onClick={this.handleAddField} className="small">
Next Stage
</button>
</div>
</div>
);
}
}
ReactDOM.render(<Jobs data={json}/>, document.getElementById('root'));
</script>
Another option is to access the props directly in your JobsRow instead of saving them in state.
<span>
<input
...
value={this.props.company}
...
/>
</span>
<span>
<input
...
value={this.props.position}
...
/>
</span>

Reactjs use function from parent

I have 2 files. One is my app.js and the other one is productmodal.js
app.js gets a productlist from an api call. It views these productst in a list.
When a user clicks on the image productmodal.js is showed.
productmodal.js shows a popup with a bigger image, productname(title) and a button called "Product niet aanwezig" (product unavailable) if the user clicks on this link a mail is send to an other user.
When the button is click I also want to activate an other function (getUpdatedProduct). This function is gonna do a long polling call to a link to check when the product is updated to a new product and update this product in the app.
The problem is: I don't know how to call the function 'getUpdatedProduct' in productmodal.js
I get an error: Uncaught TypeError: Cannot read property 'getUpdatedProduct' of undefined
I tried some of these solutions https://reactjs.org/docs/faq-functions.html. Especially the arrow function in render, because its generating a new function every time when the modal is clicked (which I need).
But nothing seems to work. Has anyone some idea's?
App.js:
import React from 'react';
import image from '../images/sogyologo.svg';
import ProductModal from './ProductModal.js';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleModal = this.toggleModal.bind(this);
this.state = {
isLoading: true,
orders: [],
dealtOrders: [],
productDetail: [],
open: false,
modal: []
}
}
toggleModal(event)
{
console.log(event);
let itemIndex = event.target.getAttribute("data-itemIndex");
console.log(itemIndex);
const productModal = this.state.orders[itemIndex];
console.log(productModal);
this.setState({
open: true,
modal: this.state.orders[itemIndex]
});
}
handleClose() {
this.setState({
open: !this.state.open
});
}
componentWillMount() {
localStorage.getItem('orders') && this.setState({
orders: JSON.parse(localStorage.getItem('orders')),
isLoading: false
})
}
componentDidMount() {
if (!localStorage.getItem('orders')){
this.fetchData();
} else {
console.log('Using data from localstorage');
}
}
fetchData() {
fetch('http://localhost:54408/api/orders/all/testing-9!8-7!6/10-04-2018')
.then(response => response.json())
.then(parsedJSON => parsedJSON.map(product => (
{
productname: `${product.ProductName}`,
image: `${product.Image}`,
quantity: `${product.Quantity}`,
isconfirmed: `${product.IsConfirmed}`,
orderid: `${product.OrderId}`
}
)))
.then(orders => this.setState({
orders,
isLoading: false
}))
.catch(error => console.log('parsing failed', error))
}
// componentWillUpdate(nextProps, nextState) {
// localStorage.setItem('orders', JSON.stringify(nextState.orders));
// localStorage.setItem('ordersDate', Date.now());
// }
render() {
this.handleDoneAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
const addToDealtOrders = dealtOrders.concat(itemToMoveAtLast);
this.setState({dealtOrders: addToDealtOrders});
this.setState({orders: prevOrders});
};
this.handleUndoAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let orders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const undoDealtOrder = dealtOrders.splice(itemIndex, 1);
const addToOrders = orders.concat(undoDealtOrder);
this.setState({orders: addToOrders});
this.setState({dealtOrders: dealtOrders});
};
const {isLoading, orders, dealtOrders} = this.state;
return (
<div>
<header>
<img src={image}/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map((order, index) => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv" onClick={this.toggleModal.bind(this) }>
<img className="img-responsive" data-itemIndex={index} src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
</div>
</div>
}) : null
}
</div>
<h2>Mandje</h2>
<div className="panel">
{
!isLoading && dealtOrders.length > 0 ? dealtOrders.map((dorder, index) => {
const {productname, image, quantity} = dorder;
return<div className="productDone" key={index}>
<div className="plaatjediv">
<img className="img-responsive" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleUndoAction}>Undo</button>
</div>
</div>
}) : null
}
</div>
<ProductModal open={this.state.open} handleClose={this.handleClose.bind(this)}
title={this.state.modal.productname} plaatje={this.state.modal.image} orderid={this.state.modal.orderid}/>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
}
export default App;
productmodal.js
import React from 'react';
class ProductModal extends React.Component {
constructor() {
super();
this.getUpdatedProduct = this.getUpdatedProduct.bind(this);
}
handleClose() {
this.props.handleClose();
}
UserAction(event) {
let orderid = event.target.value;
fetch('http://localhost:54408/api/orders/change/testing-9!8-7!6/' + orderid + '/10-04-2018');
console.log("order id = " + event.target.value);
this.getUpdatedProduct();
}
getUpdatedProduct() {
console.log("fetching new product");
}
render() {
//const open = this.props.open;
const {title, plaatje, open, orderid} = this.props;
return (
<div className={'modal fade in '+(open?'show':'')} role="dialog">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" onClick={this.handleClose.bind(this)} className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">{title}</h4>
</div>
<div className="modal-body">
<img className="plaatjediv img-responsive" src={plaatje} />
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default"
onClick={this.handleClose.bind(this)}>Sluiten
</button>
<button type="button" onClick={this.UserAction.bind()} value={orderid} className="btn btn-primary">Product niet aanwezig</button>
</div>
</div>
</div>
</div>
)
}
}
export default ProductModal;

Resources