one react component updates all the other react components - reactjs

I have a react table and one of the columns of it is another component. This component is a dropdown which get its value with an API call which I have defined in componentDidMount().
I have use case where in if user selects any value from the dropdown, I want to save that field to the DB. So I defined this post call in the handleChange function of the dropdown.
Issue is that when I change the value in any one row, every other component in other rows also calls the makes the network calls which is defined in componentDidMount(). So componentDidMount() is called for all the 4 entries. I confirmed on the server side as well. I can see four get requests(I have only 4 rows for now). I am thoroughly confused why it's behaving this way?
Parent Component
import React from 'react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import Popup from "reactjs-popup";
export default class DetailsTable extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
shipmentDataMap : { },
selectedRow: null,
downloadableAlerts: []
};
this.setState = this.setState.bind(this);
this.handleRowClick = this.handleRowClick.bind(this);
this.handleReassignment = this.handleReassignment.bind(this);
this.handleStatusUpdate = this.handleStatusUpdate.bind(this);
this.generateFilteredArr = this.generateFilteredArr.bind(this);
this.handleDownload = this.handleDownload.bind(this);
this.updateActualEntity = this.updateActualEntity.bind(this);
};
componentDidMount() {
axios.post('/entity/getRoute', {
trackingId: this.state.tid
})
.then((response) => {
let tempRoute = [];
response.data.route.forEach(element => {
tempRoute.push({ label: element['node'], value: element['node'] });
})
this.setState({route: tempRoute});
})
.catch(function (error) {
console.log(error);
});
};
updateActualEntity = (trackingId, updatedEntity) => {
let updatedRecord = this.state.shipmentDataMap[trackingId];
updatedRecord.actualEntity = updatedEntity;
this.setState({shipmentDataMap: this.state.shipmentDataMap});
};
render() {
const TableColumns = [{
Header: 'Actions',
id: 'actionPopupButton',
filterable: false,
style: {'textAlign': 'left'},
Cell: row => (<div><ReassignPopup data={row.original} updateRowFunc={this.handleReassignment} nodeOptions={this.props.nodeOptions}/>
<br/>
<UpdateStatusPopup data={row.original} updateRowFunc={this.handleStatusUpdate} statusOptions={this.props.statusOptions}/>
</div>)
},
{
Header: 'Assigned Node',
headerStyle: {'whiteSpace': 'unset'},
accessor: 'node',
style: {'whiteSpace': 'unset'}
}, {
Header: 'TID',
headerStyle: {'whiteSpace': 'unset'},
accessor: 'tid',
width: 140,
filterMethod: (filter, row) => {
return row[filter.id].startsWith(filter.value)
},
Cell: props => {props.value}
},
{
Header: 'Predicted Entity',
headerStyle: {'whiteSpace': 'unset'},
filterable: false,
accessor: 'predictedEntity',
style: {'whiteSpace': 'unset'},
},
{
Header: 'Feedback',
headerStyle: {'whiteSpace': 'unset'},
filterable: false,
accessor: 'actualEntity',
width: 140,
style: {'whiteSpace': 'unset', overflow: 'visible'},
Cell: row => (<div><AbusiveEntityComponent entity={row.original.actualEntity}
tid={row.original.tid} trackingDetailsId={row.original.trackingDetailsId}
updateActualEntityInShipmentData={this.updateActualEntity}/></div>)
}
return <div>
<CSVLink data={this.state.downloadableAlerts} filename="ShipmentAlerts.csv" className="hidden" ref={(r) => this.csvLink = r} target="_blank"/>
<ReactTable
ref={(r)=>this.reactTable=r}
className='-striped -highlight'
filterable
data={Object.values(this.state.shipmentDataMap)}
//resolveData={data => data.map(row => row)}
columns={TableColumns}
//filtered={this.state.filtered}
filtered={this.generateFilteredArr(this.props.filterMap, this.props.searchParams)}
/*onFilteredChange={(filtered, column, value) => {
this.onFilteredChangeCustom(value, column.id || column.accessor);
}}*/
defaultFilterMethod={(filter, row, column) => {
const id = filter.pivotId || filter.id;
if (typeof filter.value === "object") {
return row[id] !== undefined
? filter.value.indexOf(row[id].toString()) > -1
: true;
} else {
return row[id] !== undefined
? String(row[id]).indexOf(filter.value) > -1
: true;
}
}}
defaultPageSize={10}
//pageSize={10}
previousText='Previous Page'
nextText='Next Page'
noDataText='No intervention alerts found'
style={{
fontSize: "12px",
height: "67.4vh" // Using fixed pixels/limited height will force the table body to overflow and scroll
}}
getTheadFilterProps={() => {return {style: {display: "none" }}}}
getTbodyProps={() => {return {style: {overflowX: "hidden" }}}} //For preventing extra scrollbar in Firefox/Safari
/*
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {this.handleRowClick(e, rowInfo)},
style: {
//background: rowInfo.index === this.state.selectedRow ? '#00afec' : 'white',
color: rowInfo.index === this.state.selectedRow ? 'blue' : 'black'
}
}
} else {
return {}
}
}
} */
/>
</div>;
}
}
Child Component
import React from 'react';
import axios from 'axios';
export default class AbusiveEntityComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
entity: this.props.entity,
tid: this.props.tid,
trackingDetailsId: this.props.trackingDetailsId,
route: []
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
var selected = event.target.value;
if(selected !== '' && this.state.entity !== selected) {
if (window.confirm('Are you sure you want to select: '+ selected)) {
axios.post('/entity/upateAbusiveEntity', {
trackingDetailsId: this.state.trackingDetailsId,
abusiveEntity: selected
}).then( (response) =>{
this.setState({entity: selected});
this.props.updateActualEntityInShipmentData(this.state.tid, selected);
})
.catch(function (error) {
console.log(error);
});
}
}
}
componentDidMount() {
console.log("did mount");
axios.get('/entity/getRoute', {
params: {
trackingId: this.state.tid
}
})
.then((response) => {
let tempRoute = [];
let prev="";
response.data.route.forEach(element => {
if(prev!== "") {
tempRoute.push(prev+"-"+element['node'])
}
tempRoute.push(element['node']);
prev=element['node'];
})
this.setState({route: [''].concat(tempRoute)});
})
.catch(function (error) {
console.log(error);
});
};
render() {
return (
<div className="AbusiveEntityDiv">
<select onChange={this.handleChange} value={this.state.entity===null?'':this.state.entity}
style={{width: 100}}>
{ this.state.route.map(value => <option key={value} value={value}>{value}</option>) }
</select>
</div>
);
}
}
My question is if componentDidUpdate() is not the correct place to fetch data for dropdown, where should I define the network call ?

I found the solution. In the parent component I maintain a state of shipmentstatusmap. One of the columns of this map is acutalEntity. Now in the child component, whenever user selects the value from dropdown, I callback the parent to update the shipmentStatusMap as well. This callback was my problem.
Because now the state of parent component changes, it unmount the child and re-mount it. So it's componentDidMount is called for all the rows which in turn makes the API call.
Solution
Since I want the dropdown values only once when whole parent component is loaded, I can either move the API to constructor or the in the componentDidMount() of parent. Fetching data in constructor is not a good idea .
So I moved this API call in parent and voila! everything works as expected.
updated code:
Child component
import React from 'react';
import axios from 'axios';
export default class AbusiveEntityComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
entity: this.props.entity,
tid: this.props.tid,
trackingDetailsId: this.props.trackingDetailsId,
route: this.props.route
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
var selected = event.target.value;
if(selected !== '' && this.state.entity !== selected) {
if (window.confirm('Are you sure you want to select: '+ selected)) {
axios.post('/entity/upateAbusiveEntity', {
trackingDetailsId: this.state.trackingDetailsId,
abusiveEntity: selected
}).then( (response) =>{
this.setState({entity: selected});
this.props.updateActualEntityInShipmentData(this.state.tid, selected);
})
.catch(function (error) {
console.log(error);
});
}
}
}
render() {
return (
<div className="AbusiveEntityDiv">
<select onChange={this.handleChange} value={this.state.entity===null?'':this.state.entity}
style={{width: 100}}>
{ this.state.route.map(value => <option key={value} value={value}>{value}</option>) }
</select>
</div>
);
}
}
Parent component
import React from 'react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import Popup from "reactjs-popup";
export default class DetailsTable extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
shipmentDataMap : { },
selectedRow: null,
downloadableAlerts: []
};
this.setState = this.setState.bind(this);
this.handleRowClick = this.handleRowClick.bind(this);
this.handleReassignment = this.handleReassignment.bind(this);
this.handleStatusUpdate = this.handleStatusUpdate.bind(this);
this.generateFilteredArr = this.generateFilteredArr.bind(this);
this.handleDownload = this.handleDownload.bind(this);
this.updateActualEntity = this.updateActualEntity.bind(this);
};
// this portion was updated
componentDidMount() {
fetch('/shipment/all')
.then(res => res.json())
.then(shipmentList => {
var tidToShipmentMap = {};
var totalShipmentCount = shipmentList.length;
var loadedShipmentRoute = 0;
shipmentList.forEach(shipment => {
axios.get('/entity/getRoute', {
params: {
trackingId: shipment.tid
}
})
.then(response => {
let tempRoute = [];
let prev="";
response.data.route.forEach(element => {
if(prev!== "") {
tempRoute.push(prev+"-"+element['node'])
}
tempRoute.push(element['node']);
prev=element['node'];
})
shipment.route = [''].concat(tempRoute);
tidToShipmentMap[shipment.tid] = shipment;
loadedShipmentRoute++;
if (loadedShipmentRoute === totalShipmentCount) {
this.setState({ shipmentDataMap: tidToShipmentMap});
console.log(tidToShipmentMap);
}
})
.catch(function (error) {
console.log(error);
});
});
})
.catch(error => console.log(error));
};
updateActualEntity = (trackingId, updatedEntity) => {
let updatedRecord = this.state.shipmentDataMap[trackingId];
updatedRecord.actualEntity = updatedEntity;
this.setState({shipmentDataMap: this.state.shipmentDataMap});
};
render() {
const TableColumns = [{
Header: 'Actions',
id: 'actionPopupButton',
filterable: false,
style: {'textAlign': 'left'},
Cell: row => (<div><ReassignPopup data={row.original} updateRowFunc={this.handleReassignment} nodeOptions={this.props.nodeOptions}/>
<br/>
<UpdateStatusPopup data={row.original} updateRowFunc={this.handleStatusUpdate} statusOptions={this.props.statusOptions}/>
</div>)
},
{
Header: 'Assigned Node',
headerStyle: {'whiteSpace': 'unset'},
accessor: 'node',
style: {'whiteSpace': 'unset'}
}, {
Header: 'TID',
headerStyle: {'whiteSpace': 'unset'},
accessor: 'tid',
width: 140,
filterMethod: (filter, row) => {
return row[filter.id].startsWith(filter.value)
},
Cell: props => {props.value}
},
{
Header: 'Predicted Entity',
headerStyle: {'whiteSpace': 'unset'},
filterable: false,
accessor: 'predictedEntity',
style: {'whiteSpace': 'unset'},
},
{
Header: 'Feedback',
headerStyle: {'whiteSpace': 'unset'},
filterable: false,
accessor: 'actualEntity',
width: 140,
style: {'whiteSpace': 'unset', overflow: 'visible'},
Cell: row => (<div><AbusiveEntityComponent entity={row.original.actualEntity}
tid={row.original.tid} trackingDetailsId={row.original.trackingDetailsId}
updateActualEntityInShipmentData={this.updateActualEntity}/></div>)
}
return <div>
<CSVLink data={this.state.downloadableAlerts} filename="ShipmentAlerts.csv" className="hidden" ref={(r) => this.csvLink = r} target="_blank"/>
<ReactTable
ref={(r)=>this.reactTable=r}
className='-striped -highlight'
filterable
data={Object.values(this.state.shipmentDataMap)}
//resolveData={data => data.map(row => row)}
columns={TableColumns}
//filtered={this.state.filtered}
filtered={this.generateFilteredArr(this.props.filterMap, this.props.searchParams)}
/*onFilteredChange={(filtered, column, value) => {
this.onFilteredChangeCustom(value, column.id || column.accessor);
}}*/
defaultFilterMethod={(filter, row, column) => {
const id = filter.pivotId || filter.id;
if (typeof filter.value === "object") {
return row[id] !== undefined
? filter.value.indexOf(row[id].toString()) > -1
: true;
} else {
return row[id] !== undefined
? String(row[id]).indexOf(filter.value) > -1
: true;
}
}}
defaultPageSize={10}
//pageSize={10}
previousText='Previous Page'
nextText='Next Page'
noDataText='No intervention alerts found'
style={{
fontSize: "12px",
height: "67.4vh" // Using fixed pixels/limited height will force the table body to overflow and scroll
}}
getTheadFilterProps={() => {return {style: {display: "none" }}}}
getTbodyProps={() => {return {style: {overflowX: "hidden" }}}} //For preventing extra scrollbar in Firefox/Safari
/*
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {this.handleRowClick(e, rowInfo)},
style: {
//background: rowInfo.index === this.state.selectedRow ? '#00afec' : 'white',
color: rowInfo.index === this.state.selectedRow ? 'blue' : 'black'
}
}
} else {
return {}
}
}
} */
/>
</div>;
}
}

Related

calling setState from onClick JavaScript function not working

I am trying to create a button that will make visible a form to edit any contact on my list. However, when I press the button, nothing happens.
I have the initial state set to
this.state = {
contacts: [],
showEditWindow: false,
EditContactId: ''
};
I added a function:
editContact = (id) => {
this.setState({
showEditWindow: true, EditContactId: {id}
});
};
and a column:
{
title: "",
key: "action",
render: (record) => (
<button onClick={() => this.editContact(record.id)}
>
Edit
</button>
)
},
I imported EditContactModal and call it as
<EditContactModal reloadContacts={this.reloadContacts}
showEditWindow={this.state.showEditWindow}
EditContactId={this.state.EditContactId}/>
If I manually set this.state to showEditWindow:true, the window appears; however, either this.editContact(id) is not being called or it is not changing the state.
Calling this.deleteContact(id) works fine, as does setState in loadContacts() and reloadContacts()
What I am doing wrong?
Below are the full components.
Contacts.jsx
import { Table, message, Popconfirm } from "antd";
import React from "react";
import AddContactModal from "./AddContactModal";
import EditContactModal from "./EditContactModal";
class Contacts extends React.Component {
constructor(props) {
super(props);
this.state = {
contacts: [],
showEditWindow: false,
EditContactId: ''
};
this.editContact = this.editContact.bind(this);
};
columns = [
{
title: "First Name",
dataIndex: "firstname",
key: "firstname"
},
{
title: "Last Name",
dataIndex: "lastname",
key: "lastname"
},{
title: "Hebrew Name",
dataIndex: "hebrewname",
key: "hebrewname"
},{
title: "Kohen / Levi / Yisroel",
dataIndex: "kohenleviyisroel",
key: "kohenleviyisroel"
},{
title: "Frequent",
dataIndex: "frequent",
key: "frequent",
},{
title: "Do Not Bill",
dataIndex: "donotbill",
key: "donotbill"
},
{
title: "",
key: "action",
render: (record) => (
<button onClick={() => this.editContact(record.id)}
>
Edit
</button>
)
},
{
title: "",
key: "action",
render: (_text, record) => (
<Popconfirm
title="Are you sure you want to delete this contact?"
onConfirm={() => this.deleteContact(record.id)}
okText="Yes"
cancelText="No"
>
<a type="danger">
Delete{" "}
</a>
</Popconfirm>
),
},
];
componentDidMount = () => {
this.loadContacts();
}
loadContacts = () => {
const url = "http://localhost:3000/contacts";
fetch(url)
.then((data) => {
if (data.ok) {
return data.json();
}
throw new Error("Network error.");
})
.then((data) => {
data.forEach((contact) => {
const newEl = {
key: contact.id,
id: contact.id,
firstname: contact.firstname,
lastname: contact.lastname,
hebrewname: contact.hebrewname,
kohenleviyisroel: contact.kohenleviyisroel,
frequent: contact.frequent.toString(),
donotbill: contact.donotbill.toString(),
};
this.setState((prevState) => ({
contacts: [...prevState.contacts, newEl],
}));
});
})
.catch((err) => message.error("Error: " + err));
};
reloadContacts = () => {
this.setState({ contacts: [] });
this.loadContacts();
};
deleteContact = (id) => {
const url = `http://localhost:3000/contacts/${id}`;
fetch(url, {
method: "delete",
})
.then((data) => {
if (data.ok) {
this.reloadContacts();
return data.json();
}
throw new Error("Network error.");
})
.catch((err) => message.error("Error: " + err));
};
editContact = (id) => {
this.setState({
showEditWindow: true, EditContactId: {id}
});
};
render = () => {
return (
<>
<Table
className="table-striped-rows"
dataSource={this.state.contacts}
columns={this.columns}
pagination={{ pageSize: this.pageSize }}
/>
<AddContactModal reloadContacts={this.reloadContacts} />
<EditContactModal reloadContacts={this.reloadContacts}
showEditWindow={this.state.showEditWindow}
EditContactId={this.state.EditContactId}/>
</>
);
}
}
export default Contacts;
EditContactModal.jsx
import { Button, Form, Input, Modal, Select } from "antd";
import React from "react";
import ContactForm from './ContactForm';
const { Option } = Select;
class EditContactModal extends React.Component {
formRef = React.createRef();
state = {
visible: this.props.showEditWindow,
};
onFinish = (values) => {
const url = `http://localhost:3000/contacts/${this.props.EditContactId}`;
fetch(url, {
method: "put",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(values),
})
.then((data) => {
if(data.ok) {
this.handleCancel();
return data.json();
}
throw new Error("Network error.");
})
.then(() => {
this.props.reloadContacts();
})
.catch((err) => console.error("Error: " + err))
};
showModal = () => {
this.setState({
visible: true,
});
};
handleCancel = () => {
this.setState({
visible: false,
});
};
render() {
return (
<>
{/*<Button type="primary" onClick={this.showModal}>
Create New +
</Button>*/}
<Modal
title="Edit Contact"
visible={this.state.visible}
onCancel={this.handleCancel}
footer={null}
>
<ContactForm />
</Modal>
</>
);
}
}
export default EditContactModal;
if your aim is to perform an update to the state object, you must not pass mutable data, but copy it instead into a new object.
this will allow the state changes to be picked up.
so, prefer setState({ ...state, ...someObject }) over setState(someObject).

Why am I getting the TypeError: _this.props.data is not a function

I am using material-table to build a table of users from a call to my API. The data returns just fine in the console, but when I got to render it, I get an error. Here is an image of my error.
And my code:
import React, { useState, useEffect, useRef, Fragment } from 'react';
import axios from 'axios';
import { API } from '../../config';
import Layout from '../../components/Layout';
import MaterialTable from 'material-table';
const PendingUser = () => {
const [pendingUser, setPendingUser] = useState({
firstName: '',
lastName: '',
email: '',
agency: ''
});
const isMountedVal = useRef(1);
useEffect(() => {
isMountedVal.current = 1;
return () => {
isMountedVal.current = 0;
};
getPendingUsers();
setPendingUser(pendingUser);
}, []);
const getPendingUsers = async () => {
const { data } = await axios.get(`${API}/admin/pendinguser`);
await data.filter(user => {
user.accountApproved ? setPendingUser(user) : setPendingUser();
setPendingUser(user);
});
};
const handleClick = (name, rowData, index, email) => e => {
e.preventDefault();
try {
if (name === 'deny') {
axios.delete(`${API}/admin/pendinguser/${name}/${rowData._id}`);
} else {
name === 'approve';
axios.put(`${API}/admin/pendinguser/${name}/${rowData._id}`);
}
} catch (error) {
console.log(error);
}
};
const columns = [
{
title: 'First Name',
field: 'firstName'
},
{
title: 'Last Name',
field: 'lastName'
},
{
title: 'Email',
field: 'email'
},
{
title: 'Law Enforcement Agency',
field: 'leAgency'
},
{
title: 'Approve',
field: 'approve',
render: rowData => (
<i
className='far fa-check-circle fa-2x'
style={{ color: 'green' }}
onClick={handleClick('approve', rowData)}
></i>
)
},
{
title: 'Deny',
field: 'deny',
render: rowData => (
<i
className='far fa-times-circle fa-2x'
style={{ color: 'red' }}
onClick={handleClick('deny', rowData)}
></i>
)
},
{
title: 'Denial Reason',
field: 'denialReason',
render: rowData => (
<select>
<option value='Not Law Enforcement'>Not Law Enforcement</option>
<option value='Non US Law Enforcement'>Non US Law Enfrocement</option>
</select>
)
}
];
console.log(pendingUser);
return (
<Layout>
<MaterialTable
title='Pending Users'
columns={columns}
data={pendingUser}
isLoading={!pendingUser.length}
options={{
headerStyle: {
backgroundColor: '#249DCD',
color: 'white',
fontWeight: 'bold'
}
}}
/>
</Layout>
);
};
export default PendingUser;
If I remove the data from the columns render just fine, but what is the point if I cant get the data to render.
Material Table requires data to be either an array or a function. You are instead setting it as an object. So material-table first checks if its an array, its not, so it assumes its a function and tries to invoke it, resulting in the above error.

How to render only 5 items in react autosuggest?

I'am using react autosuggest npm package to get the json data and display it. I want to display only 5 items. How to do it?
Form.js
import React from 'react'
import Autosuggest from 'react-autosuggest';
import cities from 'cities.json';
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
// Here I get data from cities.json
return inputLength === 0 ? [] : cities.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
);
};
const getSuggestionValue = suggestion => suggestion.name;
const renderSuggestion = suggestion => (
<div>
{console.log('suggestion', suggestion)}
{suggestion.name}
</div>
);
class Form extends React.Component {
constructor() {
super();
this.state = {
value: '',
suggestions: []
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render(){
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Search City...',
value,
onChange: this.onChange
};
return (
<div>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
<br/>
</div>
)
}
}
export default Form;
I want to render only 5 items, otherwise, computer hangs while loading huge data. Is there any other autocomplete react npm package, since I want only cities and country list. i.e when city is inputted, automatically the city name must be suggested with its relevant country.Any solution or suggestion highly appreciated. Thanks in advance
i modified you're getSuggestions() method a little i guess this should work for you.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
// Here I get data from cities.json
return inputLength === 0 ? [] : cities.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
).slice(0,5);
};
Use the Slice method with start index and last Index
suggestions={suggestions.slice(0, 5)}
import {
React
,Avatar
,axiosbase
} from '../../import-files';
import Autosuggest from 'react-autosuggest';
import './autosuggest.css';
import { withStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
import Paper from '#material-ui/core/Paper';
import MenuItem from '#material-ui/core/MenuItem';
let suggestions = [ { label: 'Afghanistan' } ];
function renderInputComponent(inputProps) {
const { classes, inputRef = () => {}, ref, ...other } = inputProps;
return (
<TextField
className={classes.textField}
fullWidth
variant="outlined"
InputProps={{
inputRef: node => {
ref(node);
inputRef(node);
},
classes: {
input: classes.input,
},
}}
{...other}
/>
);
}
function renderSuggestion(suggestion, { query, isHighlighted }) {
return (
<MenuItem selected={isHighlighted} component="div">
<div>
<strong key={String(suggestion.id)} style={{ fontWeight: 300 }}>
<span className="sugg-option">
<span className="icon-wrap">
<Avatar src={suggestion.Poster}></Avatar>
</span>
<span className="name">
{suggestion.Title}
</span>
</span>
</strong>
</div>
</MenuItem>
);
}
function initSuggestions(value) {
suggestions = value;
}
function getSuggestionValue(suggestion) {
return suggestion.Title;
}
function onSuggestionSelected(event, { suggestion, suggestionValue, suggestionIndex, sectionIndex, method }) {
console.log('HandleSuggestion() '+suggestionValue);
}
const styles = theme => ({
root: {
height: 50,
flexGrow: 1,
},
container: {
position: 'relative',
},
suggestionsContainerOpen: {
position: 'absolute',
zIndex: 998,
marginTop: theme.spacing.unit,
left: 0,
right: 0,
overflowY: 'scroll',
maxHeight:'376%'
},
suggestion: {
display: 'block',
},
suggestionsList: {
margin: 0,
padding: 0,
listStyleType: 'none',
},
divider: {
height: theme.spacing.unit * 2,
},
});
class IntegrationAutosuggest extends React.Component {
state = {
single: '',
popper: '',
suggestions: [],
};
componentDidMount() {
initSuggestions(suggestions);
}
// Filter logic
getSuggestions = async (value) => {
const inputValue = value.trim().toLowerCase();
var _filter = JSON.stringify({
filter : inputValue,
});
return await axiosbase.post(`${apiCall}`, _filter);
};
handleSuggestionsFetchRequested = ({ value }) => {
this.getSuggestions(value)
.then(data => {
if (data.Error) {
this.setState({
suggestions: []
});
} else {
const responseData = [];
data.data.itemsList.map((item, i) => {
let File = {
id: item.idEnc,
Title: item.englishFullName +' '+item.arabicFullName,
englishFullName: item.englishFullName,
arabicFullName: item.arabicFullName,
Poster: item.photoPath,
}
responseData.push(File);
});
this.setState({
suggestions: responseData
});
}
})
};
handleSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
handleChange = name => (event, { newValue }) => {
this.setState({
[name]: newValue,
});
if(event.type=='click'){
if(typeof this.props.handleOrderUserFirstNameChange === "function"){
this.props.handleOrderUserFirstNameChange(newValue);
}
this.state.suggestions.filter(f=>f.Title===newValue).map((item, i) => {
//id
//Title
// Poster
if(typeof this.props.handleUserIDChange === "function"){
this.props.handleUserIDChange(item.id);
}
});
}
};
render() {
const { classes } = this.props;
// console.log('Re-render!!');
// console.log(this.props);
// console.log(this.state.suggestions);
const autosuggestProps = {
renderInputComponent,
suggestions: this.state.suggestions,
onSuggestionsFetchRequested: this.handleSuggestionsFetchRequested,
onSuggestionsClearRequested: this.handleSuggestionsClearRequested,
onSuggestionSelected: this.props.onSelect,
getSuggestionValue,
renderSuggestion,
};
return (
<div className={classes.root}>
<Autosuggest
{...autosuggestProps}
inputProps={{
classes,
placeholder: this.props.placeHolder,
value: this.state.single,
onChange: this.handleChange('single'),
}}
theme={{
container: classes.container,
suggestionsContainerOpen: classes.suggestionsContainerOpen,
suggestionsList: classes.suggestionsList,
suggestion: classes.suggestion,
}}
renderSuggestionsContainer={options => (
<Paper {...options.containerProps} square>
{options.children}
</Paper>
)}
/>
<div className={classes.divider} />
</div>
);
}
}
export default withStyles(styles)(IntegrationAutosuggest);

How to render Array data in Row of react-table

I'm new in React. Using react-table component to render data from firebase and that is working well.
constructor(props) {
super(props);
this.state = {
vehicles: []
};
}
getvehicles() {
let vehicles = [];
firebase
.database()
.ref(`vehicles`)
.once('value', snapshot => {
snapshot.forEach(level1 => {
level1.forEach(level2 => {
const vehicle = level2.val();
vehicle.pictures && vehicles.push(vehicle);
});
});
this.setState({
vehicles
});
});
}
From here Data comming in react-table
return (
<div style={style}>
<div>
<ReactTable
style={{ marginLeft: '-80%', marginRight: '-80%' }}
data={this.state.vehicles}
filterable
defaultFilterMethod={(filter, row) =>
String(row[filter.id]) === filter.value
}
columns={vehiclesColumns}
SubComponent={row => {
return ;
}}
/>
</div>
</div>
);
Problem is, that I'm getting "pictures" from database, and want to put them in "subcomponent" and I do not know how? Anyone to help?
SubComponent={row => {
return (
<div>
some code here
</div>
);
Image 1 Example of data loaded from firebase
Image 2 Example of table "click on arrow and show pics from database"
=====================
New question
Ok on the end I manage to make all together but still getting error 'imageUrls' is not defined.
For me this is nightmare to find where is problem, so is there anyone who can re-check this code totally and just make comment how to fix and where is problem?!
import React, { Component } from 'react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import firebase from 'firebase';
import Moment from 'moment';
import { storage } from 'firebase';
import _ from 'underscore';
export const getFileByPath = async query =>
await storage
.ref()
.child(query)
.getDownloadURL();
class App extends Component {
constructor(props) {
super(props);
this.state = {
vehicles: []
};
this.state = {
imageUrls: []
};
}
prepareImages = () => {
Promise.all(
_.map(this.props.images, image => {
return storage.getFileByPath(image.remoteUri);
})
).then(results =>
_.each(results, result => {
const imageUrls = this.state.imageUrls;
imageUrls.push(result);
this.setState({ imageUrls: imageUrls, loading: false });
})
);
};
componentWillMount() {
this.getvehicles();
}
getvehicles() {
let vehicles = [];
firebase
.database()
.ref(`vehicles`)
.once('value', snapshot => {
snapshot.forEach(level1 => {
level1.forEach(level2 => {
const vehicle = level2.val();
vehicle.pictures && vehicles.push(vehicle);
});
});
this.setState({
vehicles
});
});
}
render() {
const vehiclesColumns = [
{
columns: [
{
Header: 'Vehicle ID',
id: 'vehicleID',
accessor: d => d.vehicleID,
filterMethod: (filter, row) =>
row[filter.id].startsWith(filter.value)
},
{
Header: 'Terminal',
id: 'terminal',
accessor: d => d.terminal,
filterMethod: (filter, row) =>
row[filter.id].startsWith(filter.value)
},
{
Header: 'Time',
id: 'timestamp',
accessor: d => {
return Moment(d.timestamp)
.local()
.format('DD-MMMM-YYYY', 'at', true);
},
filterMethod: (filter, row) =>
row[filter.id].startsWith(filter.value)
},
{
Header: 'User',
id: 'user',
accessor: d => d.user,
filterMethod: (filter, row) =>
row[filter.id].startsWith(filter.value)
}
]
}
];
return (
<div style={style}>
<div>
<ReactTable
style={{ marginLeft: '-80%', marginRight: '-80%' }}
data={this.state.vehicles}
filterable
defaultFilterMethod={(filter, row) =>
String(row[filter.id]) === filter.value
}
columns={vehiclesColumns}
SubComponent={row => {
return (
<div>
{_.map(imageUrls, image => (
<img src={image} key={image} />
))}
</div>
);
}}
/>
</div>
</div>
);
}
}
const style = {
display: 'flex',
justifyContent: 'center'
};
export default App;
I think what you are trying to do is something like:
// Somewhere else, maybe a different doc
const SubComponent = () => {
<div>Content here</div>
}
// [...previous Code]
return (
<ReactTable
style={{ marginLeft: "-80%", marginRight: "-80%" }}
data={this.state.vehicles}
filterable
defaultFilterMethod={(filter, row) =>
String(row[filter.id]) === filter.value
}
columns={vehiclesColumns}
>
<SubComponent props={somePropsHere} />
</ReactTable>
)
Am I right? The children of a Component should not be given as a property.
Regards

How to do search in table?

I want to search with all the pagination and sorter field on place.
That is i want to call handleChange with searchkeyword.
So how can i call handleSearch and than call handleChange from within handleSearch?
import React from "react";
import { withRouter } from "react-router-dom";
import { Table, Button, Icon, Row, Col, Input } from "antd";
import axios from "axios";
const Search = Input.Search;
class App extends React.Component {
state = {
data: [],
searchValue: "",
loading: false,
visible: false,
pagination: {}
};
componentDidMount() {
this.fetch();
}
handleTableChange = (pagination, filter, sorter, value) => {
console.log(pagination, value, sorter, "Table Change");
let sorterOrder = "";
let sorterField = "";
let searchVal = "";
const pager = { ...this.state.pagination };
pager.current = pagination.current;
this.setState({
pagination: pager
});
if (value) {
console.log("inside if");
searchVal = undefined;
} else {
console.log("inside else");
searchVal = value;
}
if (sorter) {
if (sorter.order === "ascend") {
sorterOrder = "ASC";
}
if (sorter.order === "descend") {
sorterOrder = "DESC";
}
sorterField = sorter.field;
}
this.fetch({
page: pagination.current,
sortField: sorterField,
sortOrder: sorterOrder,
...filter,
value: searchVal
});
};
fetch = (params = {}) => {
this.setState({ loading: true });
axios.post("***/****", params).then(res => {
const pagination = { ...this.state.pagination };
let apiData = res.data.result;
if (res.data.status === 1) {
const objects = apiData.map(row => ({
key: row.id,
id: row.id,
firstName: row.firstName,
lastName: row.lastName,
status: row.status,
email: row.email
}));
console.log(res.data);
pagination.total = res.data.count;
this.setState({
loading: false,
data: objects,
pagination
});
} else {
console.log("Database status is not 1!");
}
});
};
handleSearch = value => {
console.log("value", value);
this.setState({
searchValue: value
});
let searchkey = this.state.searchValue;
const pagination = { ...this.state.pagination };
const sorter = { ...this.state.sorter };
console.log("search", value, pagination, sorter);
this.handleTableChange({
value
});
};
render() {
const columns = [
{
title: "First Name",
dataIndex: "firstName",
sorter: true
},
{
title: "Email",
dataIndex: "email",
sorter: true
}
];
return (
<div>
<Search
placeholder="input search text"
className="searchBut"
onSearch={value => this.handleSearch(value)}
/>
<Button
className="addBut"
style={{ marginTop: "0" }}
type="primary"
onClick={() => this.openForm()}
>
+ Add Admin
</Button>
</div>
<Table
bordered
columns={columns}
dataSource={this.state.data}
pagination={{ total: this.state.pagination.total, pageSize: 4 }}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
);
}
}
export default withRouter(App);
here when i give value in search field it will call post request with request payload as follows:
{sortField: "", sortOrder: ""}
sortField: ""
sortOrder: ""
So how can i do that?
I'm not sure what you trying of achieve it here.
But you can always filter the source data of the table. Like following
<Table
bordered
columns={columns}
dataSource={this.state.data.filter(data=> Object.keys(data).filter(key => data[key].includes(searchValue)).length > 0 ? true: false)}
pagination={{ total: this.state.pagination.total, pageSize: 4 }}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
let me know if it helps.

Resources