how to implement react js pagination - reactjs

i am using react-js-pagination.
i am able to fetch the data and can show the list of data. but i am trying to impelment paggination using react-js-pagination. i am able to show paggination bar in button but not able to get functionality.
here i am trying show 3 records per page.
UI
<div style={pannelFooter}>
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={3}
totalItemsCount={this.state.projectList.length}
pageRangeDisplayed={5}
onChange={this.handlePageChange}
/>
</div>
Method
handlePageChange(pageNumber) {
this.setState({ activePage: pageNumber });
}
Constructor
constructor(props) {
super(props);
this.state = {
activePage: 1,
projectList: [],
originalProjectList: []
};
this.handlePageChange = this.handlePageChange.bind(this);
}
FUll Component
import React, { Component } from 'react';
import ReactDOM from "react-dom";
import Pagination from "react-js-pagination";
import {
BrowserRouter as Router,
Route,
IndexRoute,
Link,
} from 'react-router-dom';
import ProjectDetails from './ProjectDetails';
import DashboardContainer from '../UIcomponent/DashboardContainer';
const pannelWidth = {
width: '90%'
};
const pannelHeader = {
color: 'white'
};
const pannelFooter = {
float: 'right'
};
class ProjectList extends Component {
constructor(props) {
super(props);
this.state = {
activePage: 1,
searchText: '',
isdiagram:true,
isMax:false,
projectList: [],
originalProjectList: []
};
//this.handlePageChange = this.handlePageChange.bind(this);
this.projectDetails = this.projectDetails.bind(this);
this.deleteMessage = this.deleteMessage.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
this.setSize=this.setSize.bind(this);
}
componentDidMount() {
let d = '';
$.get("http://localhost:8008/api/navigation/all", function (data) {
d = data;
this.setState({
projectList: d,
originalProjectList: d
});
}.bind(this));
}
handlePageChange(pageNumber) {
this.setState({ activePage: pageNumber });
console.log(this.state.projectList);
}
projectDetails(item, index) {
console.log(index);
}
deleteMessage(item, index) {
showconfrim("Do you want to delete this Project?", this.deleteProject(item, index));
console.log('delete');
}
deleteProject(item, index) {
$("#confirmwindow").modal('hide');
console.log('delete');
}
setSize(){
this.setState({
isMax:!this.state.isMax
});
if(!this.state.isMax){
//clear style for jquery animate;
$(this.refs.selfdiv).attr("style",null);
setTimeout(()=>{
$(this.refs.selfdiv).animate({
top:'0px',
right: '0px',
bottom: '0px',
left: '0px'
},500);
},100);
}
console.log(this.props.children);
if(this.props.children[1].props['data-event']){
var self=this;
setTimeout(()=>{
self.props.children[1].props['data-event'].call();
},700);
}
}
updateInputValue(event) {
this.setState({
searchText: event.target.value
}, function () {
let textToSearch = this.state.searchText;
let originalData = this.state.projectList;
if (textToSearch != undefined || textToSearch != '') {
let searchData = [];
for (var i = 0; i < this.state.projectList.length; i++) {
if (this.state.projectList[i].name.indexOf(textToSearch) != -1 || this.state.projectList[i].description.indexOf(textToSearch) != -1) {
searchData.push(this.state.projectList[i]);
}
}
this.setState({
projectList: searchData
});
}
if(textToSearch == '') {
this.setState({
projectList: this.state.originalProjectList,
});
}
});
}
render() {
var listItems = this.state.projectList.map((item, index) => {
return <tr key={index}>
<td onClick={e => this.projectDetails(item, index)}><a><u>{item.name}</u></a></td>
<td>{item.description}</td>
<td><i className="glyphicon glyphicon-trash" onClick={e => this.deleteMessage(item, index)}></i></td>
</tr>
});
return (
<div className="container" style={pannelWidth} ref="selfdiv">
<br />
<div className="panel panel-primary">
<div className="panel-heading">
<div className="row">
<div className="col-md-2 col-lg-2">
<h4 style={pannelHeader}>Project List</h4>
</div>
<div className="col-md-6 col-lg-6">
<input type="text" className="form-control" placeholder="Search" value={this.state.searchText} onChange={this.updateInputValue}/>
</div>
<div className="col-md-2 col-lg-2">
<button className="btn btn-sm btn-success">Create New Project</button>
</div>
<div className="col-md-2 col-lg-2">
<div className="captiontoolbar buttoncontainer">
<span onClick={this.setSize} style={pannelFooter} className={
this.state.isMax ? ("boxMaxsize glyphicon glyphicon-resize-small") : ("boxMaxsize glyphicon glyphicon-fullscreen")
}></span>
</div>
</div>
</div>
</div>
<div className="panel-body">
<table className="table table-striped">
<thead>
<tr>
<th><b>Project Name</b></th>
<th><b>Description</b></th>
<th><b>Action</b></th>
</tr>
</thead>
<tbody>
{listItems}
</tbody>
</table>
</div>
<div style={pannelFooter}>
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={3}
totalItemsCount={this.state.projectList.length}
pageRangeDisplayed={5}
onChange={this.handlePageChange.bind(this)}
/>
</div>
</div>
</div>
);
}
}
export default ProjectList;

var indexOfLastTodo = this.state.activePage * this.state.itemPerPage;
var indexOfFirstTodo = indexOfLastTodo - this.state.itemPerPage;
var renderedProjects = this.state.projectList.slice(indexOfFirstTodo, indexOfLastTodo);
var listItems = renderedProjects.map((item, index) => {
return <tr key={index}>
<td onClick={e => this.projectDetails(item, index, e)}><a><u>{item.projectName}</u></a></td>
<td>{item.description}</td>
<td><i className="glyphicon glyphicon-trash" onClick={(e) => { if (window.confirm('All its related data will be deleted. Are you sure you want to delete?')) this.deleteMessage(item, index) } } > </i></td>
<td><i className="glyphicon glyphicon-edit" id="edit" onClick={e => this.editProject(item, index, e)}></i></td>
</tr>
});
Here need to update the projectList array using slice(firstIndex, lastIndex). then subarray should be use for rander purpose.
and paggination tag should be like below
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={this.state.itemPerPage}
totalItemsCount={this.state.originalProjectList.length}
pageRangeDisplayed={5}
onChange={this.handlePageChange.bind(this)}
/>

Take advantage of ES6.
So, insted of doing something like this:
this.handlePageChange = this.handlePageChange.bind(this);
You can do this:
handlePageChange = (pageNumber) => {
console.log(`active page is ${pageNumber}`);
this.setState({activePage: pageNumber});
}
Hope this helps.

Related

How can I pass a boolean parameter to another component?

What I'm trying to pass true/false in another component. The user has the possibility to add languages(max2). When the user adds 2 same languages, the validation error will be shown, which to do that I have used some() method which returns true or false.
I tried it with ```localStorge``, but it didn't work.
import React from "react";
export default class AddLanguage extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "",
items: [],
hasError: ""
};
}
updateMessage(event) {
this.setState({
message: event.target.value
});
}
handleClick() {
var items = this.state.items;
items.push(this.state.message);
this.setState({
items: items,
message: ""
});
}
handleItemChanged(i, event) {
const items = event.target.value;
console.log("items", items);
this.setState((prev) => {
const currList = prev.items;
console.log("currList", currList);
const isDuplicate = currList.some(
(lang, idx) => idx !== i && lang) === items
);
if (isDupliacte) {
return { ...prev, hasError: "A BIG ERROR" };
} else {
currList[i] = items;
return { ...prev, items: currList };
}
});
}
handleItemDeleted(i) {
var items = this.state.items;
items.splice(i, 1);
this.setState({
items: items
});
}
renderRows() {
var context = this;
return this.state.items.map(function (o, i) {
return (
<tr key={"item-" + i}>
<td>
<div>
<input
type="text"
value={o}
autoComplete="off"
onChange={context.handleItemChanged.bind(context, i)}
/>
</div>
{this.state.hasError && (
<div>
<label></label>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
className="inline-block mr-2 h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
</svg>{" "}
{this.state.hasError}
{""}
</div>
</div>
)}
</td>
<td >
<button
type="button"
onClick={context.handleItemDeleted.bind(context, i)}
>
Delete
</button>
</td>
</tr>
);
}, this);
}
render() {
return (
<div>
<div>
<table>
<thead">
<tr>
<th
>
Button
</th>
<th
>
Info
</th>
<th
>
ACTION
</th>
</tr>
</thead>
<tbody>
{this.renderRows()}
</tbody>
</table>
</div>
<div>
<button
className="btn-main"
disabled={this.state.items.length >= 2}
onClick={this.handleClick.bind(this)}
>
<PlusSmIcon />
Add
</button>
</div>
</div>
);
}
}
What I want to pass to the other component is isDuplicate (which might be true/false to another component.

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.

Need to pass the value for same component

I need to pass value for same component
I need to pass my value here
<tr>
<td><b>Past</b></td>
<td className="hrs_applicant_data"><b>tes</b></td>
</tr>
Here Is my code
import React, { Component } from 'react';
import axios from 'axios';
import {UIPageSubNav} from '../../helpers/UI';
import {SpaceDiv} from '../../helpers/Generic';
//import ViewJobs from './ViewJobs';
//import PostJob from './PostJob';
import ViewApplicantDetails from './ViewApplicantDetails';
import Dropdown from 'react-dropdown';
import 'react-dropdown/style.css';
import {Tabs, Tab} from 'react-bootstrap-tabs';
import Popup from 'react-popup';
export default class ViewApplicants extends Component {
constructor(props) {
super(props);
this.state = {
//applicantResults:[],
//searchExperiencesResults: [],
//searchMessagesResults: [],
jobTitleList: null
};
}
// onReloadPage(callbackRef) {
// console.dir(callbackRef);
// this.onGetApplicants(callbackRef);
// }
onClickFitStaus(){
}
onClickApplicantDetails(personId){
console.log(personId.props.applicantData.PersonId);
var personId = personId.props.applicantData.PersonId;
var self = this;
axios.get(window.mg.api.baseURL + 'applicantdetails' + '/'+ personId, { // use id because personId != id
})
.then(function (response) {
var items = [];
var Element = [];
var index = [];
var experience: []
//console.log(response.data.results.experience);
var experience = response.data.results.experience;
console.log(experience);
//return false;
var applicantResult = response.data.results.main[0];
var id = personId;
//console.log(experience);
// self.setState({applicantResults: response.data.results.main[0]});
// self.setState({searchMessagesResults: response.data.results.message});
// self.setState({searchExperiencesResults: response.data.results.experience});
// let mySpecialPopup = Popup.register({
// title: 'I am special',
// content: 'Since I am special you might need me again later. Save me!',
// buttons: {
// left: ['cancel'],
// right: ['ok']
// }
// });
// Popup.alert('Hello');
// Popup.create({
// title: 'Immediate popup',
// content: 'This popup will be displayed straight away',
// className: 'alert',
// buttons: {
// right: ['ok']
// }
// }, true);
//Popup.queue(mySpecialPopup);
window.mg.popupRef.customPopup(
<ViewApplicantDetails applicantData={applicantResult} pid={id} customPopup={true} />
);
//check if we need to tell the callee to update too (React's smart update needed or not doesn't alway work the way I want)
})
.catch(function (error) {
console.log('error');
console.dir(error);
window.mg.popupRef.alert('Error getting data, please try again in a minute.');
});
}
// onClick(e) {
// // passes our data back up to the Employees component
// this.props.onClickRef(this.props.applicantData);
// }
onClickUpdateFit(fitStatus, personId){
// if (this.props.hasOwnProperty('userData') === false) {
// return (<div></div>);
// }
console.log(personId);
console.log(this.props.screenData);
var userId = 2;
axios.post(window.mg.api.baseURL + 'fitstatusupdate', {
headers: {
'X-CSRF-TOKEN': window.mg.api.token,
'Content-Type':'application/x-www-form-urlencoded'
},
params: {
personId: personId,
fitStatus: fitStatus,
message: null,
userId: userId
}
}).then(response => {
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
console.log('Error', thrown.message);
}
});
}
render() {
var self = this;
var applicant = this.props.applicantData;
const jobtitle = this.state.jobTitleList;
console.log(applicant);
return (
<div className="hrs_applicant_main_container">
<div className="sod_applicant_container">
<div className="sod_job_company_logo">
<img src={applicant.imgUrl} />
</div>
<div className="sod_job_details">
<div className="sod_job_header">
<div className="sod_job_info">
<h3 onClick={this.onClickApplicantDetails.bind(applicant.personId,this)}>{applicant.Name}</h3>
<span>{applicant.location}, {applicant.country}</span>
</div>
<div className="sod_job_salary">
<div>
<span><b>Date Uploaded</b></span>
<span>{applicant.created_at}</span>
</div>
</div>
<div className="sod_job_location">
<img src="../../images/icon_upload.png" />
<div>
<span><b>CV</b></span>
</div>
</div>
</div>
<div className="sod_job_content">
<div className="hrs_applicant_details">
<table className="hrs_applicant_datas">
<tr>
<td><b>Current</b></td>
<td className="hrs_applicant_data"><b>{applicant.created_at}</b></td>
</tr>
<tr>
<td><b>Past</b></td>
<td className="hrs_applicant_data"><b>tes</b></td>
</tr>
<tr>
<td><b>Education</b></td>
<td className="hrs_applicant_data"><b>CIPD, MCIPD | 2013</b></td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div className="hrs_applicant_action">
<div className="BTNFilled BTNOutlineFilled" onClick={() => this.onClickUpdateFit(0, applicant.PersonId)}>
<i class="fa fa-times"></i> <img src="/images/img_close.png" alt="" className="notFitIconImg" />
NOT A FIT
</div>
<div className="BTNFilled BTNOutlineFilled" onClick={() => this.onClickUpdateFit(1, applicant.PersonId)}>
<img src="/images/img_tick.png" alt="" className="fitIconImg" />
GOOD FIT
</div>
<div className="BTNFilled BTNOutlineFilled" onClick={this.onClickApplicantDetails.bind(applicant.personId,this)}>
<img src="/images/message-icon.png" alt="" className="fitIconImg" />
MESSAGE
</div>
<div className="BTNFilled BTNOutlineFilled DropdownButton">
<Dropdown options={jobtitle} onChange={this._onSelect} placeholder="ASSOCIATE TO JOB POSTING" />
</div>
</div>
</div>
)
var BTNStyle = {};
BTNStyle.marginRight = "10px";
BTNStyle.marginLeft = "0px";
BTNStyle.marginBottom = "30px";
}
}
Experience is a list of values right so you can display those values through map function. I will give one example below
this.state.experience = [{name:"anu",age:"21",email:"anu#gmail.com"},{name:"sai",age:"21",email:"sai#gmail.com"}]
render(){
return(
this.state.experience.map((user,index)=>{
return <p key={index}>{user.name} </p>
})
)
}

Set Component State Synchronously on ComponentWIllReveiveProps

I am passing props from Dashboard component to Modal Component when I click an item in the Dahboard table row. When the row is clicked, the data in that row is passed as props to the modal and the modal set its state on ComponenentWIllReceiveProps synchronously so it renders in the Modal Input box and textarea
How do I set the state in modal with props passed down from Dashboard.js and render them in the modal inpputbox and textarea
Dashboard.js
import React, { Component } from 'react'
import Modal from '../Modal/Modal'
import add from '../../images/add.png'
import addSelected from '../../images/addSelected.png'
import './Dashboard.css'
const TableRow = ({ row, openQuoteDetails, deleteQuote }) => (
<tr>
<th scope="row" onClick={openQuoteDetails}>{row.author}</th>
<td onClick={openQuoteDetails}>{row.quote}<small id="admin" className="form-text text-muted">{row.admin}</small></td>
<td><i className="fa fa-close" onClick={deleteQuote}></i></td>
</tr>
)
const Table = ({ data, openQuoteDetails, deleteQuote }) => (
<table className="table table-hover">
<thead>
<tr className="table-active">
<th scope="col">Author</th>
<th scope="col">Quote</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{data.map((row, index) =>
<TableRow key={index} row={row} openQuoteDetails={() => openQuoteDetails(row, index)} deleteQuote={() => deleteQuote(row, index)} />
)}
</tbody>
</table>
)
class Dashboard extends Component {
constructor() {
super()
this.state = {
quotes: [
{
"quote": "Our industry does not respect tradition - it only respects innovation.",
"author": "Satya Nadella",
"admin": "Joseph Akayesi"
},
{
"quote": "Engineering is the closest thing to magic that exists in the world.",
"author": "Elon Musk",
"admin": "Joseph Akayesi"
},
{
"quote": "For me, it matters that we drive technology as an equalizing force, as an enabler for everyone around the world.",
"author": "Sundar Pichai",
"admin": "Yasmin Adams"
}
],
addSource: add,
isModalOpen: false,
index: '',
author: '',
quote: ''
}
}
onAddMouseOver = () => {
this.setState({ addSource: addSelected })
}
onAddMouseOut = () => {
this.setState({ addSource: add })
}
toggleModalOpenOrClose = () => {
this.setState({ isModalOpen: !this.state.isModalOpen })
this.setState({ index: '' })
this.setState({ author: '' })
this.setState({ quote: '' })
}
openQuoteDetails = (row, index) => {
this.setState({ isModalOpen: true });
this.setState({ index: index, author: row.author, quote: row.quote })
}
deleteQuote = (row, index) => {
this.setState({ isModalOpen: false })
console.log('Row deleted')
console.log(this.state.quotes.splice(index, 1))
}
render() {
return (
<div className='pt-3'>
<Table
data={this.state.quotes}
openQuoteDetails={this.openQuoteDetails}
deleteQuote={this.deleteQuote} />
<div className='text-center align-items-center justify-content-centerpt-5'>
<a href='#add' onClick={this.toggleModalOpenOrClose}>
<img src={this.state.addSource} className='addButton mx-1' alt="add" onMouseOver={this.onAddMouseOver} onMouseOut={this.onAddMouseOut} />
</a>
</div>
<Modal
isModalOpen={this.state.isModalOpen}
toggleModalOpenOrClose={this.toggleModalOpenOrClose}
data={this.state}
onInputChange={this.onInputChange}
addNewQuote={this.addNewQuote}
updateExistingQuote={this.updateExistingQuote} />
</div>
)
}
}
export default Dashboard
Modal.js
import React, { Component } from 'react'
import './Modal.css'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { addQuote } from '../../actions/quoteActions'
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
id: '',
author: '',
quote: '',
errors: {}
}
}
onInputChange = (event) => {
this.setState({ [event.target.id]: event.target.value })
}
switchSaveChangesAction = () => {
return this.props.state.index ? this.addNewQuote : this.updateExistingQuote
}
addNewQuote = () => {
const { user } = this.props.auth
const newQuote = {
admin: user.id,
quote: this.state.quote,
author: this.state.author,
}
console.log('Add New')
console.log(newQuote)
}
componentWillReceiveProps(nextProps) {
console.log('receive props')
if(nextProps.author !== this.props.author){
this.setState({ author: nextProps.author})
}
// this.setState({ id: this.props.data.index })
console.log(nextProps)
this.setState({ author: this.props.data.author }, () => console.log(this.state.author))
console.log(this.state)
}
updateExistingQuote = (index) => {
console.log('Update Existing')
console.log(this.props.state.author)
console.log(this.props.state.quote)
console.log(this.props.state.index)
}
render() {
let showOrHideModal = this.props.isModalOpen ? 'modal d-block' : 'modal d-none'
// let selectedQuoteDetails = {
// id: this.props.data.index ? this.props.data.index : '',
// author: this.props.data.author ? this.props.data.author : '',
// quote: this.props.data.quote ? this.props.data.quote : ''
// };
// let modalInputValue = selectedQuoteDetails ? selectedQuoteDetails : this.state
let saveChangesAction = this.props.data.index >= 0 ? this.updateExistingQuote : this.addNewQuote
return (
<div className={showOrHideModal}>
<div className='modal-dialog' role='document'>
<div className='modal-content'>
<div className='modal-header bg-light'>
<h5 className='modal-title'><b>Add a Quote</b></h5>
<button type='button' className='close' data-dismiss='modal' aria-label='Close' onClick={this.props.toggleModalOpenOrClose}>
<span aria-hidden='true'>×</span>
</button>
</div>
<div className='modal-body'>
<div className='form-group'>
<label htmlFor='author'>Author</label>
<input type='text' className='form-control' id='author' aria-describedby='emailHelp' placeholder='Enter author' onChange={this.onInputChange} defaultValue={this.state.author} />
</div>
<div className='form-group'>
<label htmlFor='quote'>Quote</label>
<textarea className='form-control' id='quote' rows='3' placeholder='Enter quote' onChange={this.onInputChange} value={this.state.quote}></textarea>
</div>
</div>
<div className='modal-footer'>
<button type='button' className='btn btn-primary' onClick={saveChangesAction}>Save changes</button>
<button type='button' className='btn btn-secondary' data-dismiss='modal' onClick={this.props.toggleModalOpenOrClose}>Close</button>
</div>
</div>
</div>
</div>
)
}
}
Modal.propTypes = {
addQuote: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
}
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
})
export default connect(mapStateToProps, { addQuote })(Modal)

Issue with leave animation using React-Flip-Move

I'm using this module for my twitch API app: https://github.com/joshwcomeau/react-flip-move/
and currently having an issue with the leave animation. The enter animation works perfectly, fine, but unforunately, when I click 'x' on one of the channels, the element (in my case a ) moves up and to the right. How do I make it fade out in its current position?
import React, { Component } from 'react';
import { connect } from 'react-redux';
import FlipMove from 'react-flip-move';
import { selectUser, fetchUser, removeUser } from '../actions/index';
class UsersList extends Component {
constructor(props) {
super(props);
this.state = {
show: 'all',
};
this.fetchInitialUsers(this.props.initialUsers);
}
fetchInitialUsers(users) {
users.map(this.props.fetchUser);
}
renderUser(user) {
const { channelData, streamData } = user;
return (
<tr
key={channelData.display_name}
onClick={() => this.props.selectUser(user)}
className='list-item'>
<td>
<img src={channelData.logo} className='user-logo' />
</td>
<td>
{channelData.display_name}
</td>
<td>
{streamData.stream ?
<span className='online'>Online</span> :
<span className='offline'>Offline</span>}
</td>
<span
className="glyphicon glyphicon-remove"
onClick={() => this.props.removeUser(user)}></span>
</tr>
)
}
showOnline() {
this.setState({
show: 'online'
});
}
showOffline() {
this.setState({
show: 'offline'
});
}
showAll() {
this.setState({
show: 'all'
});
}
render() {
return (
<div className='col-sm-4'>
<div className='text-center'>
<div className='btn-group btn-group-sm' role='group'>
<button
className='btn btn-default'
onClick={this.showAll.bind(this)}>
All
</button>
<button
className='btn btn-default'
onClick={this.showOnline.bind(this)}>
Online
</button>
<button
className='btn btn-default'
onClick={this.showOffline.bind(this)}>
Offline
</button>
</div>
</div>
<div className='container'>
<table className='table table-hover'>
<thead>
<tr>
<th>Logo</th>
<th>Channel</th>
<th>Status</th>
</tr>
</thead>
{/* <tbody> */}
<FlipMove
typeName='tbody' enterAnimation='fade'
leaveAnimation='fade'>
{this.props.users.filter(user => {
const { show } = this.state;
const { streamData } = user;
if (show == 'online') {
return streamData.stream;
}
else if (show == 'offline') {
return !streamData.stream;
}
else {
return user;
}
}).map(this.renderUser.bind(this))}
</FlipMove>
{/* </tbody> */}
</table>
</div>
</div>
)
}
}
function mapStateToProps({ users, initialUsers }) {
return { users, initialUsers };
}
export default connect(mapStateToProps, { selectUser, fetchUser, removeUser })(UsersList);
just add maintainContainerHeight="true" to the flipmove attributes

Resources