Pagination Component not rendered on UI - React - javascript

I am trying to implement pagination in my react application using this guide I created the Pagination.js file as instructed in the guide, but I am not able to see that on my UI, here is the screenshot of the application
Here is my Search Results Page where I am implementing pagination, basically this will show the results fetched from the server based on user entered keyword and hence I want to show as paginated results. My js file corresponding to the above screenshot is:
import React from 'react';
import NavigationBar from './NavigationBar';
import SearchPageResultsStyle from "../assets/css/SearchResultsPage.css"
import Pagination from './Pagination';
class SearchResultsPage extends React.Component{
constructor(props) {
super(props);
console.log("Printing in the results component: this.props.location.state.data.keyword")
console.log(this.props.location.state.data.keyword)
this.state = {
results: this.props.location.state.data.results,
keyword: this.props.location.state.data.keyword,
pageOfItems: []
};
this.onChangePage = this.onChangePage.bind(this);
}
onChangePage(pageOfItems) {
// update local state with new page of items
this.setState({pageOfItems});
}
render() {
return(
<div>
<NavigationBar/>
<h4 style={{textAlign:'center', color:'#1a0dab'}}>Showing search results for <span style={{fontWeight:'bold', fontStyle:'Italic'}}>'{this.state.keyword}'</span></h4>
<hr/>
<div className={'wrap'} style={SearchPageResultsStyle}>
<div className={'fleft'}>left column</div>
<div className={'fcenter'}>
<h3 style={{color:'#1a0dab'}}>Tweeter tweets text will be displayed here!!!</h3>
<a href={'https://google.com'}>Tweet urls will be displayed here</a>
<br/>
<div style={{display:'inline'}}>
<p><span style={{fontWeight:'bold', textColor:'#6a6a6a'}}>topic: </span>crime</p>
<p><span style={{fontWeight:'bold', textColor:'#6a6a6a'}}>city: </span>delhi</p>
<p><span style={{fontWeight:'bold', textColor:'#6a6a6a'}}>lang: </span>Hindi</p>
<p><span style={{fontWeight:'bold', textColor:'#6a6a6a'}}>Hashtags: </span></p>
<hr/>
<Pagination items={this.state.results} onChangePage={this.onChangePage}/>
</div>
</div>
<div className={'fright'}>right column</div>
</div>
</div>
)
}
}
export default SearchResultsPage;
My pagination.js file
import React from 'react';
import PropTypes from 'prop-types';
const propTypes = {
items: PropTypes.array.isRequired,
onChangePage: PropTypes.func.isRequired,
initialPage: PropTypes.number,
pageSize: PropTypes.number
};
const defaultProps = {
initialPage: 1,
pageSize: 10
};
class Pagination extends React.Component{
constructor(props){
super(props);
this.state = {
pager: {}
};
// set page if items array isn't empty
if (this.props.items && this.props.items.length) {
this.setPage(this.props.initialPage);
}
}
componentDidUpdate(prevProps, prevState) {
// reset page if items array has changed
if (this.props.items !== prevProps.items) {
this.setPage(this.props.initialPage);
}
}
setPage(page) {
var { items, pageSize } = this.props;
var pager = this.state.pager;
if (page < 1 || page > pager.totalPages) {
return;
}
// get new pager object for specified page
pager = this.getPager(items.length, page, pageSize);
// get new page of items from items array
var pageOfItems = items.slice(pager.startIndex, pager.endIndex + 1);
// update state
this.setState({ pager: pager });
// call change page function in parent component
this.props.onChangePage(pageOfItems);
}
getPager(totalItems, currentPage, pageSize) {
// default to first page
currentPage = currentPage || 1;
// default page size is 10
pageSize = pageSize || 10;
// calculate total pages
var totalPages = Math.ceil(totalItems / pageSize);
var startPage, endPage;
if (totalPages <= 10) {
// less than 10 total pages so show all
startPage = 1;
endPage = totalPages;
} else {
// more than 10 total pages so calculate start and end pages
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage + 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage + 4;
}
}
// calculate start and end item indexes
var startIndex = (currentPage - 1) * pageSize;
var endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);
// create an array of pages to ng-repeat in the pager control
var pages = [...Array((endPage + 1) - startPage).keys()].map(i => startPage + i);
// return object with all pager properties required by the view
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
render() {
var pager = this.state.pager;
if (!pager.pages || pager.pages.length <= 1) {
// don't display pager if there is only 1 page
return null;
}
return (
<div>
<ul className="pagination">
<li className={pager.currentPage === 1 ? 'disabled' : ''}>
<button onClick={() => this.setPage(1)}>First</button>
</li>
<li className={pager.currentPage === 1 ? 'disabled' : ''}>
<button onClick={() => this.setPage(pager.currentPage - 1)}>Previous</button>
</li>
{pager.pages.map((page, index) =>
<li key={index} className={pager.currentPage === page ? 'active' : ''}>
<button onClick={() => this.setPage(page)}>{page}</button>
</li>
)}
<li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}>
<button onClick={() => this.setPage(pager.currentPage + 1)}>Next</button>
</li>
<li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}>
<button onClick={() => this.setPage(pager.totalPages)}>Last</button>
</li>
</ul>
</div>
);
}
}
Pagination.propTypes = propTypes;
Pagination.defaultProps = defaultProps;
export default Pagination;
I do not understand that why my list of items in Pagination.js file is not getting rendered.
Can anybody point out what exactly is it that I am missing?

Your issue is a misplaced if statement in the constructor. So this:
class Pagination extends React.Component{
constructor(props){
super(props);
this.state = {
pager: {}
};
// set page if items array isn't empty
if (this.props.items && this.props.items.length) {
this.setPage(this.props.initialPage);
}
}
Should be:
class Pagination extends React.Component{
constructor(props){
super(props);
this.state = {
pager: {}
}
}
componentWillMount() {
if (this.props.items && this.props.items.length) {
this.setPage(this.props.initialPage);
}
}

This is how I implemented the above library, it's really cool library. I have to add css to it, to make it look good. It is working for me now.
import React from 'react';
import NavigationBar from './NavigationBar';
import SearchPageResultsStyle from "../assets/css/SearchResultsPage.css"
import Pagination from './Pagination';
class SearchResultsPage extends React.Component{
constructor(props) {
super(props);
console.log("Printing in the results component: this.props.location.state.data.results")
console.log(this.props.location.state.data.results)
this.state = {
results: this.props.location.state.data.results,
keyword: this.props.location.state.data.keyword,
pageOfItems: []
};
this.onChangePage = this.onChangePage.bind(this);
}
onChangePage(pageOfItems) {
// update local state with new page of items
this.setState({pageOfItems});
}
render() {
const renderItems = this.state.pageOfItems.map((item, index) => {
return (
<div>
<h3 style={{color: '#1a0dab'}} key={index}>{item.text}</h3>
<a href={'https://google.com'} key={index}>{item.tweetUrl}</a>
<br/>
<p><span style={{fontWeight:'bold', textColor:'#6a6a6a'}} key={index}>topic: </span>{item.topic}</p>
<p><span style={{fontWeight:'bold', textColor:'#6a6a6a'}} key={index}>city: </span>{item.city}</p>
<p><span style={{fontWeight:'bold', textColor:'#6a6a6a'}} key={index}>lang: </span>{item.lang}</p>
<p><span style={{fontWeight:'bold', textColor:'#6a6a6a'}} key={index}>Hashtags: </span></p>
<hr/>
</div>
)
})
return (
<div>
<NavigationBar/>
<h4 style={{textAlign:'center', color:'#1a0dab'}}>Showing search results for <span style={{fontWeight:'bold', fontStyle:'Italic'}}>'{this.state.keyword}'</span></h4>
<hr/>
<div className={'wrap'} style={SearchPageResultsStyle}>
<div className={'fleft'}>left column</div>
<div className={'fcenter'}>
{renderItems}
<Pagination items={this.state.results} onChangePage={this.onChangePage}/>
</div>
</div>
<div className={'fright'}></div>
</div>
)
}
}
export default SearchResultsPage;

Related

Show contents of array in render

I am using MERN stack and Redux. I have created an array in the state 'comments' which is updated via the clickHandler function with elements from the global state (accessed via props). When i try to show the contents of the array in the render i just get the length of it. How would i show the properties of the elements for example title.
import React, { Component } from "react";
import PropTypes from "prop-types";
import GoogleSearch from "./GoogleSearch";
import { connect } from "react-redux";
import { fetchSubjects } from "../../actions/subject";
import { fetchComments } from "../../actions/comment";
import store from "../../store";
class Subject extends Component {
// on loading the subjects and comments
// are fetched from the database
componentDidMount() {
this.props.fetchSubjects();
this.props.fetchComments();
}
constructor(props) {
super(props);
this.state = {
// set inital state for subjects description
// and summary to invisible
viewDesription: -1,
viewSummary: -1,
comments: [],
};
}
componentWillReceiveProps(nextProps) {
// new subject and comments are added to the top
if (nextProps.newPost) {
this.props.subjects.unshift(nextProps.newPost);
}
if (nextProps.newPost) {
this.props.comments.unshift(nextProps.newPost);
}
}
clickHandler = (id) => {
// when a subject title is clicked pass in its id
// and make the desciption visible
const { viewDescription } = this.state;
this.setState({ viewDescription: viewDescription === id ? -1 : id });
// clear the existing comments in state
this.setState({
comments: [],
});
// loop through the comment items in the global state
// and add any with the same subjects id passed in to the array
var i;
for (i = 0; i < this.props.comments.length; i++) {
if (this.props.comments[i].subject == id) {
console.log(this.props.comments[i]);
this.setState({
comments: this.state.comments.unshift(this.props.comments[i]),
});
}
} // set local storage to the id for the subject that has been clicked
localStorage.setItem("passedSubject", id);
};
// hovering on and off subjects toggles the visibility of the summary
hoverHandler = (id) => {
this.setState({ viewSummary: id });
};
hoverOffHandler = () => {
this.setState({ viewSummary: -1 });
};
render() {
const subjectItems = this.props.subjects.map((subject) => {
// if the state equals the id set to visible if not set to invisible
var view = this.state.viewDescription === subject._id ? "" : "none";
var hover = this.state.viewSummary === subject._id ? "" : "none";
var comments = this.state.comments;
return (
<div key={subject._id}>
<div
className="subjectTitle"
onClick={() => this.clickHandler(subject._id)}
onMouseEnter={() => this.hoverHandler(subject._id)}
onMouseLeave={() => this.hoverOffHandler()}
>
<p className="title">{subject.title}</p>
<p className="rating">Rating: {subject.rating}</p>
<p className="summary" style={{ display: hover }}>
{subject.summary}
</p>
</div>
<div className="subjectBody " style={{ display: view }}>
<div className="subjectAuthor">
<p className="author">
Subject created by: {subject.author} on {subject.date}
</p>
<a href="">
<div className="buttonRateSubject">RATE SUBJECT</div>
</a>
</div>
<div className="subjectDescription">
<p className="description">{subject.description}</p>
</div>
<div className="subjectLinks">Links:</div>
<div className="subjectComments">
<p>Comments:</p>
{/* ************HERE*********** */}
<p>{comments}</p>
{/* ********************************* */}
<a href="/addcomment">
<div className="buttonAddComment">ADD COMMENT</div>
</a>
</div>
</div>
</div>
);
});
return (
<div id="Subject">
<GoogleSearch />
{subjectItems}
</div>
);
}
}
Subject.propTypes = {
fetchSubjects: PropTypes.func.isRequired,
fetchComments: PropTypes.func.isRequired,
subjects: PropTypes.array.isRequired,
comments: PropTypes.array.isRequired,
newPost: PropTypes.object,
};
const mapStateToProps = (state) => ({
subjects: state.subjects.items,
newSubject: state.subjects.item,
comments: state.comments.items,
newComment: state.comments.item,
});
// export default Subject;
export default connect(mapStateToProps, { fetchSubjects, fetchComments })(
Subject,
Comment
);
I think I know your problem. You want to render items of an array.
Let me just give you a short overview.
Javascript:
this.setState({
comments: data
});
render (){
return (
<div>
{ this.state.comments.map(c=> <div>{c.body}</div> ) }
</div>
)
}
Thanks guys, i changed the for loop in the clickHandler to this which now has data rendering, it didn't like objects in the array for some reason.
var temp = [];
for (i = 0; i < this.props.comments.length; i++) {
if (this.props.comments[i].subject == id) {
console.log(this.props.comments[i]);
temp.unshift(this.props.comments[i].comment);
temp.unshift(this.props.comments[i].title);
}
}
this.setState({
comments: temp,
});

ReactJS rendering issue with edited array

Why does ReactJS remove the last element when the array is different after removing the middle element when using array.splice?
This is my code. I am using React-Redux.
const reducerNotesAndLogin = (state = initialState, action) => {
var tableNotes = "notities";
var tableCategories = "categories";
switch(action.type){
case "CATEGORY_REMOVE":
// Remove the category
var newCategories = state.categories;
console.log("state.categories", state.categories);
console.log("before: ", {newCategories});
var index = 0;
for(var i = 0; i < newCategories.length; i++){
if(newCategories[i].id === action.payload.categoryId){
newCategories.splice(i, 1);
index = i;
i--;
}
}
console.log("after: ", {newCategories});
state = {
...state,
categories: newCategories
}
break;
default:
break;
}
return state;
}
export default reducerNotesAndLogin;
Output below (I deleted the middle element. My web app always removes the last element of the categories (but not from the array).
Step 1: Initial state
Step 2: Remove middle item, expecting the middle item to be removed.
Step 3: Confusion
Why is the array correct, but the view incorrect? I am updating the state.categories correctly right?
This is my render code (as is - without filtering away any other code that mihgt be important)
CategoriesBody:
import React from 'react';
import { connect } from 'react-redux';
import CategoryItem from './CategoryItem';
import Button from './../../Button';
import store from '../../../redux/store-index';
class CategoriesBody extends React.Component {
render(){
return (
<div>
<ul className="list--notes">
{this.props.categories.map((category) => {
if(category.id === undefined){ // No categories
return <li>No categories</li>
} else {
return (
<div>
<CategoryItem category={category} />
<div className="mb-small hidden-sm hidden-md hidden-lg"> </div>
</div>
);
}
})}
</ul>
</div>
);
}
}
function mapStateToProps(state){
return {
categories: state.reducerNotesAndLogin.categories,
categoriesLength: state.reducerNotesAndLogin.categories.length
};
}
export default connect(mapStateToProps)(CategoriesBody);
CategoriesItem.js:
import React from 'react';
import store from './../../../redux/store-index';
import Button from './../../Button';
class CategoryItem extends React.Component {
constructor(props){
super();
this.state = {
edit: false,
categoryName: props.category.categoryName,
categoryColor: props.category.categoryColor
}
this.onClickEdit = this.onClickEdit.bind(this);
this.onChangeCategoryColor = this.onChangeCategoryColor.bind(this);
this.onChangeInputCategoryName = this.onChangeInputCategoryName.bind(this);
this.onClickEditSave = this.onClickEditSave.bind(this);
this.onClickEditCancel = this.onClickEditCancel.bind(this);
}
removeCategory(id, name){
console.log("nsvbsvbfjvbdjhbvv");
store.dispatch({ type: "CATEGORY_REMOVE", payload: {
categoryId: id
}});
// store.dispatch({type: "NOTIFY", payload: {
// type: 'success',
// message: 'Category "' + name + '" removed!'
// }});
}
onClickEdit(){
this.setState({
edit: true
});
}
onChangeCategoryColor(e){
this.setState({
categoryColor: e.target.value
});
}
onChangeInputCategoryName(e){
this.setState({
categoryName: e.target.value
});
}
onClickEditSave(){
this.setState({
edit: false,
categoryName: this.state.categoryName,
categoryColor: this.state.categoryColor
});
store.dispatch({type: "CATEGORY_EDIT", payload: {
categoryId: this.props.category.id,
categoryName: this.state.categoryName,
categoryColor: this.state.categoryColor
}});
store.dispatch({type: "NOTIFY", payload: {
type: "success",
message: "Category saved!"
}});
}
onClickEditCancel(){
this.setState({
edit: false,
categoryName: this.props.category.categoryName,
categoryColor: this.props.category.categoryColor
});
}
render(){
return (
<li key={this.props.category.id} className={this.state.edit === true ? "mt mb" : "flex-justify-between flex-align-center"}>
<div className={this.state.edit === true ? "d-none" : ""}>
<div className="input--color" style={{
backgroundColor: this.state.categoryColor
}}> </div>
{this.state.categoryName}
</div>
{/* Mobile */}
<div className={this.state.edit === true ? "d-none" : "hidden-sm hidden-md hidden-lg"}>
<Button onClick={() => this.onClickEdit()} buttonType="primary">Edit</Button>
<div className="mt-small"> </div>
<Button onClick={() => this.removeCategory(this.props.category.id, this.props.category.categoryName)} type="primary">Remove</Button>
</div>
{/* Tablet and desktop */}
<div className={this.state.edit === true ? "d-none" : "hidden-xs"}>
<div style={{float:'left',}}><Button onClick={() => this.onClickEdit()} buttonType="primary">Edit</Button></div>
<div style={{float:'left',marginLeft:'15px'}}><Button onClick={() => this.removeCategory(this.props.category.id, this.props.category.categoryName)} type="primary">Remove</Button></div>
</div>
{/* EDITING STATE */}
<div className={this.state.edit === true ? "" : "d-none"}>
<div className="row">
<div className="col-xs-12">
<input onChange={this.onChangeCategoryColor} className="input--wide" type="color" value={this.state.categoryColor}
style={{backgroundColor: this.state.categoryColor, height: '30px'}}
/>
<input onChange={this.onChangeInputCategoryName} className="input--wide" type="text" value={this.state.categoryName} />
</div>
</div>
<div className="row mt">
<div className="col-xs-12">
<Button buttonType="primary" onClick={() => this.onClickEditSave()}>Save</Button>
</div>
</div>
<div className="row mt-small">
<div className="col-xs-12">
<Button buttonType="secondary" onClick={() => this.onClickEditCancel()}>Cancel</Button>
</div>
</div>
</div>
</li>
)
}
}
export default CategoryItem;
I think it has something to do with the rendering. Because the arrays are correct when I console.log them. Only the view is different...
Do not modify the state in reducer directly. Create a copy of state value and then modify it.
Change:
var newCategories = state.categories;
To:
var newCategories = [...state.categories];
You should not modify the same array while looping through it.
for (var i = 0; i < newCategories.length; i++) {
if (newCategories[i].id === action.payload.categoryId) {
newCategories.splice(i, 1);
index = i;
i--;
}
}
I got the answer after looking through it with a friend of mine. The solution is pretty simple...
Lesson 101: Make sure that you have a unique "key" property when looping through an array in your UI.
The solution is to add this to my code:
<div key={category.id}>
{this.props.categories.map....
...
</div>

ReactJS: Reset-Render Child Component on Parent state change

I have a strange - at least for me :-) - issue with my Components.
I have a map() function generating a list of Child elements. When I change the filter and the list repopulates the state of the children remain as it was. For example, if the second Alumni Child Component has state {height:auto} the new second Allumni Child has again {height: auto}.
I find it really odd because this is not the same element, it's a new element corresponding to another person, with new content passed through props.
Long story short how do I force my new Child elements to have initial state {height: 0}?
There is a lot of details hidden but the core of my Parent App is as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
filterName: "",
filterInitial: "Α",
....
filteredGraduates:[],
loaderVisible:true
};
this.updateInitial = this.updateInitial.bind(this)
...
}
componentDidMount(){
...
}
updateInitial(letter) {
if (letter==='*'){
this.setState({
filterInitial: "*",
filteredGraduates : [...graduates]
})
}else{
this.setState({
filterInitial: letter,
filteredGraduates : graduates.filter( (graduate) => graduate.Surname.charAt(0)===letter )
})
}
console.log('this is the letter: ',letter);
}
render() {
return (
<div className="app">
<div className="filters">
<div className="name-pagination">
<span
onClick={ () => this.updateInitial('*')}
className={'*'==this.state.filterInitial? 'active': ''}
>Α - Ω
</span>
<hr/>
{letters.map( (letter) => {
return(
<span
onClick={ () => this.updateInitial(letter)}
className={letter==this.state.filterInitial? 'active': ''}
>{letter}</span>
)
}
)}
</div>
</div>
</div>
{this.state.loaderVisible && <Loader /> }
{!this.state.loaderVisible && <div className="graduates-wrapper">
{this.state.filteredGraduates
.sort( (x,y) => x.Surname.localeCompare(y.Surname) )
.map( (graduate) => {
return(
<div>
<Allumni
key={graduate.id}
.... a lot of props ...
/>
</div>
)
})
}
</div>}
</div>
);
}
}
and my Child is:
class Allumni extends React.Component {
constructor(props) {
super(props);
this.state = {
height:0
};
this.updateHeight = this.updateHeight.bind(this)
}
updateHeight() {
this.setState({
height: this.state.height === 0 ? 'auto' : 0,
});
};
render() {
const {
name,
surName,
.......
} = this.props
return (
<div className="allumni-wrapper">
<div className="allumni-main-info">
<span className="allumni-surname">{surName}</span><br/><span className="allumni-name">{name}</span>
</div>
<div className="allumni-extra">
<span className="allumni-year">{yearOf}</span><br/>
<span className="allumni-job">{job}</span> / <span className="allumni-home-city">{homeCity}</span>
</div>
<div className="allumni-details">
<AnimateHeight
duration={ 500 }
height={ this.state.height }
>
<p><span className="attribute-name">... {fatherName}</p>
<p><span className="attribute-name">...{studies}</p>
....
</AnimateHeight>
</div>
<div className="allumni-actions" onClick={this.updateHeight}>
<AccountCardDetailsOutlineIcon size={30}/>
<span className={'actions-toggle ' + (this.state.height >= 0 ? '' : 'rotated') }><ChevronDownIcon/></span>
</div>
</div>
);
}
}
export default Allumni
thank you

prevent duplicate while merging redux array of objects with setState array

First of all one general question that how to concat setState array with redux array? In my implementation, there is a configured list which is based on setState array of objects. Now, i have a location list which is based on redux array of objects. Now i am adding one item from location list to configured list using concat and saving the data. It is successfully saved but in merged list it is displaying 3 items (that redux array item 2 times).
But, i checked in console although it is showing correct result(2 items) but why it is displaying 3 items(redux item 2 times).
Component code:(getLocationData method where i did the concatenation part and calling that method in other component using callback ref and working fine)
export class NewLocationPanel extends React.Component{
constructor(props){
super(props);
this.state={
open:false,
configuredList:[],
retrievedList:[]
};
this.configLocation = this.configLocation.bind(this);
this.togglePanel = this.togglePanel.bind(this);
this.handleClick = this.handleClick.bind(this);
this.allLocations = this.allLocations.bind(this);
this.clearall = this.clearall.bind(this);
this.getLocationData = this.getLocationData.bind(this);
this.handleRemove = this.handleRemove.bind(this);
this.removeConfigLocation = this.removeConfigLocation.bind(this);
this.mergedLocation = this.mergedLocation.bind(this);
}
togglePanel (e){
this.setState({open : !this.state.open});
}
handleRemove(mruCode){
this.props.removeLocation(mruCode)
}
handleClick (mruCode){
this.props.addLocation(mruCode)
}
allLocations (){
this.props.addAllLocation()
}
clearall (){
this.props.removeAllLocation()
}
componentDidMount() {
this.props.loadData();
if(this.props.locationData !=null && this.props.locationData!= undefined){
this.configLocation(this.props.locationData);
}
}
componentDidUpdate(prevProps,prevState){
if ((prevProps.jobId != this.props.jobId || prevProps.locationData != this.props.locationData) && this.props.locationData != null && this.props.locationData != undefined) {
this.configLocation(this.props.locationData);
this.mergedLocation();
}
}
configLocation(locationData){
let configuredList =[];
if(locationData.locations.locationDetails != null && locationData.locations.locationDetails !=undefined ){
locationData.locations.locationDetails.map(item=>{
let listitem ={...item};
configuredList.push(listitem);
});
}
this.setState({configuredList},()=>{
console.log(this.state.configuredList);
});
}
removeConfigLocation(index){
this.setState({
configuredList:this.props.locationData.locations.locationDetails.filter((_,i)=>i!==index)
},()=>{
console.log(this.state.configuredList);
});
}
mergedLocation(){
if(this.props.conLocations != null && this.state.configuredList !=null){
const{configuredList} = this.state;
let retrievedList = configuredList;
this.props.conLocations.forEach(loct => {
const locationAdded = retrievedList.find(_loct=>loct.mruCode=== loct.mruCode)
});
this.setState({
retrievedList},()=>{
console.log(this.state.retrievedList);
});
}
}
getLocationData(){
let saveableLocationlist = [];
if(this.state.retrievedList != null){
saveableLocationlist = retrievedList;
}
const locationData = {
locationDetails : saveableLocationlist
}
return locationData;
}
render(){
//const{configuredList} = this.state;
const _labels = store.getLabels();
let collapsedToggle = this.props.open ? 'collapsed' : ''
return(
<div className="panel panel-default">
<div className="panel-heading" onClick={(e)=>this.togglePanel(e)}>
<div className="row">
<div className="col-xs-12 col-sm-8 col-md-6 col-lg-6 panelHeadingLabel">
<span>{this.props.title}</span>
</div>
<div className="pull-right">
<span className="defaultHeaderTextColor">{this.state.configuredList.map((loc,index)=><span key={index}>{loc.mruCode} - {_labels[loc.division]} - {loc.country}</span>)}
<span onClick={(e)=>this.togglePanel(e)} className={this.state.open ? "collapse-chevronn" : "collapse-chevron"} aria-hidden="true"></span>
</span>
</div>
</div>
</div>
{this.state.open?(
<div className="panel-body">
<div className="row grid-divider">
<div className="col-sm-6">
<div className="col-padding"><div className="pos-div"><h3>Locations List</h3><button style={{ display: this.props.location.length === this.props.conLocations.length ? "none" : "block" }} className="allLargeBtn" onClick={()=>{this.allLocations()}}>Add all locations</button></div><hr/>
{this.props.location.map((item,index)=>(
<div key={index}><div><b>{item.mruCode} - {_labels[item.division]} - {item.country}</b>{!this.props.conLocations.find(item2 => item.mruCode === item2.mruCode)&&(<div className="pull-right jd"><button style={{ display: this.state.configuredList.find(item3=> item.mruCode===item3.mruCode) ? "none" : "block" }} className="call-to-action" onClick={()=>{this.handleClick(item.mruCode)}}>Add Location</button></div>)}<hr/></div></div>))}
</div>
</div>
<div className="col-sm-6">
<div className="col-padding">
<div className="pos-div"><h3>Configured Location</h3><button className="allLargeBtn" onClick={()=>this.clearall()}>Remove all location</button></div><hr/>
<div><table className="table"><tbody>{this.state.retrievedList.map((locc,index)=><tr key={index}><td><b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b></td><td className="text-right"><img alt="DeleteIcon" onClick={()=>{this.removeConfigLocation(index)}} className="deleteIconStyle" src="img/delete_large_active.png" /></td></tr>)}
</tbody></table></div>
</div>
</div>
</div>
</div>):null}
</div>
);
}
}
const mapStateToProps = state =>{
return{
location:state.locationRed.location,
conLocations:state.locationRed.conLocations
};
};
const mapDispatchToProps = (dispatch) => {
return{
loadData:()=>{dispatch(loadData())},
addLocation:(mruCode)=>{dispatch(addLocation(mruCode))},
addAllLocation:() =>{dispatch(addAllLocation())},
removeLocation: (mruCode)=>{dispatch(removeLocation(mruCode))},
removeAllLocation: () =>{dispatch(removeAllLocation())}
}
}
export default connect(mapStateToProps,mapDispatchToProps,null,{withRef:true})(NewLocationPanel);
Jobs Component(where i am calling getLocationData to save updated value of jobs...It is saving the details properly)
import React from 'react';
import ReactDOM from 'react-dom';
import LocationPanel from '../panels/NewLocationPanel';
class JobsPanelComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
jobDetailJson: this.props.jobDetailJson
};
this.setLocationPanelRef = cRef =>{this.locationPanel = cRef;};
}
componentWillUnmount() {
this.clearStates();
this.clearRefs();
this.clearBindings();
}
clearStates() {
this.state.jobDetailJson = null;
}
clearRefs(){
this.locationPanel = null;
}
clearBindings(){
this.setLocationPanelRef = null;
}
componentWillMount() {
this.state.jobDetailJson = this.props.jobDetailJson;
}
componentWillReceiveProps(nextProps) {
this.state.jobDetailJson = nextProps.jobDetailJson;
}
saveJobData(jobData){
var locationData = null;
if(some conditions){
locationData = this.locationPanel.getWrappedInstance().getLocationData();
}
//more other lines not related to my mine
}
render(){
var locationDataJson= null;
if(this.state.jobDetailJson != null){
locationDataJson =this.state.jobDetailJson;
}
return(<div className="panel-group" id="jobsPanelGroup">
<LocationPanel ref={this.setLocationPanelRef} locationData ={locationDataJson} jobDetailJson={this.state.jobDetailJson} versionId={versionId} jobName={jobName} jobId={jobId} isForViewOnly={this.props.isForViewOnly} parentJobId={this.props.parentJobId} title="Location"/>
//More coded lines for other things not related to my part
);
}
}
I am adding current output which is showing 3 results but it should show 2 results. How to prevent that.Please help me on this.
UPDATE #1:
mergeLocationData(){
let mergedList = [];
// you either have to setup initial state for conLocations in your reducer to `undefined` or to `null`.
if(this.props.conLocations !== undefined && this.state.configuredList !== null){
const { configuredList } = this.state;
const { conLocations } = this.props;
mergedList = configuredList;
this.props.conLocations.forEach(location => {
const locationAdded = mergedList.find(_location => _location.mruCode === location.mruCode);
if(!locationAdded){
mergedList.push(location)
}
});
}
// instead of setting state, return the mergedList
return mergedList; //[ always an array of elements ]
}
And then, at the render method: instead of getting the list from the state, we just invoke the function.
<thead>
{
this.mergeLocationData().map((locc,index)=> (
<tr key={index}>
<th>
<b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b>
</th>
<th className="text-right">
<img
alt="DeleteIcon"
onClick={()=>{this.removeConfigLocation(index)}}
className="deleteIconStyle"
src="img/delete_large_active.png" />
</th>
</tr>
)
}
</thead>
There is nothing wrong with the code, I suppose it's correctly working, but, it's the logic that is not right, you are rendering both of the arrays in your render, you are not using the function getLocationData anyware in your component, eventhough, concat won't solve the problem.
You can do the following steps to fix the logic.
- Fix the logic of getLocationData:
mergeLocationData(){
// you either have to setup initial state for conLocations in your reducer to `undefined` or to `null`.
if(this.props.conLocations !== undefined && this.state.configuredList !== null){
const { configuredList } = this.state;
const { conLocations } = this.props;
let mergedList = configuredList;
this.props.conLocations.forEach(location => {
const locationAdded = mergedList.find(_location => _location.mruCode === location.mruCode);
});
this.setState({
mergedList
});
}
}
Use the function as a callback to your fetch request, supposibly in componentDidMount after loading the data correctly.
Render the new Array of mergedList which should be in your state by now, and don't forget to add mergedList: [] to your state.
Basically you need to replace the two maps that render location elements in your render with that.
<thead>
{
this.state.mergedList.map((locc,index)=> (
<tr key={index}>
<th>
<b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b>
</th>
<th className="text-right">
<img
alt="DeleteIcon"
onClick={()=>{this.removeConfigLocation(index)}}
className="deleteIconStyle"
src="img/delete_large_active.png" />
</th>
</tr>
)
}
</thead>

File sharing web app using React and Node js

I am making a file sharing web app using a MERN development stack.
While connecting my file sharing screen to my updating screen, I got stuck.
ComponentsWillReceiveProps() is not working in the current version of React. I tried to find an alternative and it showed either set
UNSAFE_ComponentsWillReceiveProps() or use the function static getDerivedStateFromProps(nextProps, prevState), but I don't know how to define prevState.
My code is:
import React,{Component} from 'react'
import _ from "lodash"
import PropTypes from 'prop-types'
class HomeUploading extends Component{
constructor(props) {
super(props);
this.state = {
data: null,
event: null,
loaded: 0,
total: 0,
percentage: 10,
}
}
componentDidMount() {
const {data} = this.props;
this.setState({
data: data
});
}
static getDerivedStateFromProps(nextProps,prevState){
const {event} = nextProps;
console.log("Getting an event of uploading", event,prevState);
switch (_.get(event, 'type')) {
case 'onUploadProgress' :
const loaded = _.get(event, 'payload.loaded', 0);
const total = _.get(event, 'payload.total', 0);
const percentage = (total !== 0) ? ((loaded / total) * 100) : 0;
this.setState ({
loaded: loaded,
total: total,
percentage: percentage
});
break;
default:
break;
}
this.setState({
event:event,
}) ;
}
render() {
const {percentage, data, total, loaded} = this.state;
const totalFiles = _.get(data, 'files', []).length;
return (
<div className={'app-card app-card-uploading'}>
<div className={'app-card-content'}>
<div className={'app-card-content-inner'}>
<div className={'app-home-uploading'}>
<div className={'app-home-uploading-icon'}>
<i className={'icon-cloud-upload'}/>
<h2>Sending...</h2>
</div>
<div className={'app-upload-files-total'}>Uploading {totalFiles} files</div>
<div className={'app-progress'}>
<span style={{width: `${percentage}%`}} className={'app-progress-bar'}/>
</div>
<div className={'app-upload-stats'}>
<div className={'app-upload-stats-left'}>{loaded}Bytes/{total}Bytes</div>
<div className={'app-upload-stats-right'}>456K/s</div>
</div>
<div className={'app-form-actions'}>
<button className={'app-upload-cancel-button app-button'} type={'button'}>Cancel
</button>
</div>
</div>
</div>
</div>
</div>
)
}
}
HomeUploading.propTypes={
data: PropTypes.object,
event: PropTypes.object
}
export default HomeUploading;
Just glancing at the code, I'd refactor this a bit to keep this component as simple as possible (dumb/presentational component) that just displays new props as they're fed in.
Here's a description :https://medium.com/#thejasonfile/dumb-components-and-smart-components-e7b33a698d43
I'd suggest lifting the state up a level and doing the data transformation in the parent.
Something like this for the HomeUploading:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
const HomeUploading = ({
data,
event,
loaded,
total,
percentage,
}) => (
<div className={'app-card app-card-uploading'}>
<div className={'app-card-content'}>
<div className={'app-card-content-inner'}>
<div className={'app-home-uploading'}>
<div className={'app-home-uploading-icon'}>
<i className={'icon-cloud-upload'} />
<h2>Sending...</h2>
</div>
<div className={'app-upload-files-total'}>Uploading {data.length} files</div>
<div className={'app-progress'}>
<span style={{ width: `${percentage}%` }} className={'app-progress-bar'} />
</div>
<div className={'app-upload-stats'}>
<div className={'app-upload-stats-left'}>
{loaded}Bytes/{total}Bytes
</div>
<div className={'app-upload-stats-right'}>456K/s</div>
</div>
<div className={'app-form-actions'}>
<button className={'app-upload-cancel-button app-button'} type={'button'}>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
);
HomeUploading.propTypes = {
data: PropTypes.object,
event: PropTypes.object,
};
export default HomeUploading;
And then in the parent, you could have a the logic for transforming the data in a function:
import React, { Component } from 'react';
class Parent extends Component {
state = {
data: null,
event: null,
loaded: 0,
total: 0,
percentage: 0,
}
calcPercentage(loaded, total) {
return (total !== 0) ? ((loaded / total) * 100) : 0
}
render() {
const {
data,
event,
loaded,
total
} from this.state;
return (
<HomeUploading
data={data}
event={event}
loaded={loaded}
total={total}
percentage={this.calcPercentage(loaded, total)}
/>
);
}
}
export default Parent;
This approach will give you the props updating without relying on componentWillReceiveProps.

Categories