React setState not rendering view of bootstrap table - reactjs

I have this component that has a Bootstrap Table and a Modal. User should be able to go into the modal and change the state of the same data for both the table and the modal; however, I am seeing that it is only changing the view in the modal but not the table?
Component with Table and Modal:
export class TableAndModal extends React.Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
showModal: false,
index: ""
}
};
this.setShow = this.setShow.bind(this);
this.handleShowAndChange = this.handleShowAndChange.bind(this);
}
columns = [{
dataField: "view",
text: "View"
formatter: (cell, row, rowIndex) => {
return (
<div>
<Button variant="info" onClick={() => this.setShow(rowIndex)}>View</Button>
</div>
);
}
},
{dataField: 'fullName', text: 'Full Name' },
{dataField: 'studentDesc', text: 'Student Description'},
{dataField: 'email', text: 'Email'},
{dataField: 'fullNotes', text: 'Full Notes'},
{dataField: 'edu', text: 'Education'},
{dataField: 'phone', text: 'Phone Number'},
{dataField: 'id', text: 'ID'}];
setShow(index) {
this.setState({
showModal: true,
index: index
});
}
handleShowAndChange = (name, value) => {
this.setState((prevState) => {
let newState = {...prevState};
newState[name] = value;
return newState;
});
};
render() {
return (
<div>
<BootstrapTable
hover
condensed={true}
bootstrap4={true}
keyField={'id'}
data={this.state.data.info}
columns={this.columns}
/>
<Modal data={this.state.data.info} index={this.state.index}
showModal={this.state.showModal} onChange={this.handleShowAndChange} />
</div>
);
}
}
Modal:
this.state = {
data: this.props.data
};
handleInfoChange = (index) => (name, value) => {
let info = this.state.data.info.slice();
info[index][name] = value;
this.props.onChange("info", info);
};
I am seeing that the state is being modified correctly. However, the table still has the same view with the old state data even though the state has been changed.
Can someone guide me on what I am doing wrong?

I think you should use props.data instead of this.props.data
constructor(props) {
super(props);
this.state = {
data: props.data,
showModal: false,
index: ""
}
}

Related

How to trigger componentdidmount on click react

I have an application to display at a table. I am fetching api from django rest framework, the API is paginated. So when I load the react app it loads the first page( for example it callshttp://localhost:8000/cluster/37/tasks?page=1) by default. I have a next button.I am trying to go to the next page(for example it should call http://localhost:8000/cluster/37/tasks?page=2) on clicking next.
How do I attempt to trigger the fetch on clicking the next button? Thanks.
Here is sample of my code below :
class TasksApp extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1,
columnDefs: [
{ headerName: 'Temp RowNumber', valueGetter: 'node.rowIndex'},
{ headerName: 'Status', field: 'status' },
{ headerName: 'Params', field: 'joint_params' },
{ headerName: 'Total Pages', field: 'total_pages' },
{ headerName: 'Total Results', field: 'total_results' },
],
defaultColDef: { resizable: true },
rowData: null,
dataLength: 0,
id: this.props.location.state.id,
task: this.props.match.params.value,
headerHeight: 39,
rowHeight: 49,
paginationPageSize: 200,
totalPages: null,
currentPage: null,
pageSize: null,
pageNumberList: [],
pageSizeList: [],
startIndex: 0,
endIndex: 5,
};
}
onGridReady = params => {
this.gridApi = params.api;
};
componentDidMount(){
fetch(`http://localhost:8000/cluster/${this.state.id}/tasks?page=${this.state.page}`, options)
.then(res => res.json())
.then(data => this.setState({
rowData: data['results'],
dataLength: data['totalDataOnPage'],
totalData: data['totalData'],
currentPage: data['currentPage'],
totalPages: data['totalPages'],
nextLink: data['nextPage'],
previousLink: data['previousPage']
}))
.catch(err => console.log(err))
}
onPaginationChanged = () => {
let list_pages = []
if (this.gridApi) {
console.log('total pages', this.state.totalPages)
for (var i = 1; i <= this.state.totalPages; i++) {
list_pages.push(i);
}
this.setState({ pageNumberList: list_pages })
}
};
onBtNext = () => {
//how do I trigger the componentDidMount to load the next page number on click next
var url = new URL(this.state.nextLink);
var pagenumber = url.searchParams.get("page");
this.setState({ page: pagenumber })
}
render() {
const pagelist = this.state.pageNumberList.slice(this.state.startIndex, this.state.endIndex)
return (
<>
//code truncated to show the `next`, the table and another part of pagination was here.
.......
next button below..
{!pagelist.includes(this.state.totalPages) ?
<PageDirection onClick={() => this.onBtNext()} style={{marginLeft: '15px'}}>
<ChevronRight style={{ padding: '5px' }} />
</PageDirection>
: null
}
</PaginationSectorTwo>
</div>
</Fragment>
</TableContent>
</InnerWrapper>
</Wrapper>
</>
)
}
}
export default TasksApp;
You shouldn't try to trigger componentDidMount on click because componentDidMount is executed only once in the lifecycle of the component, immediately after a component is mounted.
You should change onBtNext() function to fetch the data.
Cant you just add an onClick event on the button to fetch again?
<button onClick={() => call your api method here}><button/>

Material-table ReactJs toggle detailPanel open/closing from actions

Is there a way to control the detailPanel opening/closing in material-table from the onClick() => {} action in the actions column ?
Eventualy I made it work by following this thread
export class Services extends React.Component {
constructor() {
super();
}
this.tableRef = React.createRef();
render() {
return (
<MaterialTable
tableRef={this.tableRef}
icons={tableIcons}
title= 'Service catalog table'
columns={[{ title: "Service Name", field: "nom_service", editable: 'never'}]}
actions={[
(rowData) => {
return ({
onClick: (event, rowData) => {
this.tableRef.current.onToggleDetailPanel( [rowData.tableData.id],
this.tableRef.current.props.detailPanel[0].render)
}
})}
]}
detailPanel={[
{
icon: ()=> null,
openIcon: ()=> null,
disabled:true,
render: rowData => {
return (<p>{rowData.nom_service}</p>)
}
}
]}
)
}
}

having trouble getting redux components to re-render

I have 2 components (a form for inputing values and a react-table component to display the inputed values) that I am putting on a dashboard. When I enter the values into the form to update the redux store, I can see the changes from redux tools however, the table component doesn't update until I go to a different page and come back. Anyone know what I am doing wrong?
Here is my reducer. I don't believe I am mutating the state here.
Reducer:
const keysReducerDefaultState = [];
export default (state = keysReducerDefaultState, action) => {
switch (action.type) {
case 'ADD_KEYS':
return [
...state,
action.keyPair
];
case 'REMOVE_KEYS':
return state.filter(({ name }) => {
return name !== action.name;
});
default:
return state;
}
}
Component 1
class KeysImportForm extends React.Component {
constructor(props) {
super(props);
this.state = {
// type validation
name: "",
publicKey: "",
privateKey: "",
};
this.typeClick = this.typeClick.bind(this);
}
render() {
const { classes } = this.props;
return (
// a form that takes in 3 fields, name, publickey and privatekey
);
}
}
const mapDispatchToProps = (dispatch) => ({
addKeys: (keyPair) => dispatch(addKeys(keyPair))
});
export default withStyles(validationFormsStyle)(connect(undefined, mapDispatchToProps)(KeysImportForm));
Component 2
class KeysTable extends React.Component {
constructor(props) {
super(props);
const data = props.keys.map((prop, key) => {
return {
id: key,
name: prop.name,
publicKey: prop.publicKey,
privateKey: prop.privateKey,
};
});
this.state = {
data
};
}
render() {
const { classes } = this.props;
return (
<GridContainer>
<GridItem xs={12}>
<Card>
<CardHeader color="primary" icon>
<CardIcon color="primary">
<Assignment />
</CardIcon>
<h4 className={classes.cardIconTitle}>Key Pairs</h4>
</CardHeader>
<CardBody>
<ReactTable
data={this.state.data}
filterable
columns={[
{
Header: "Name",
accessor: "name",
minWidth: 10
},
{
Header: "Public Key",
accessor: "publicKey",
minWidth: 50
},
{
Header: "Private Key",
accessor: "privateKey",
minWidth: 50
},
{
Header: "Action",
accessor: "action",
minWidth: 10,
sortable: false,
filterable: false
}
]}
defaultPageSize={10}
showPaginationTop
showPaginationBottom={false}
className="-striped -highlight"
/>
</CardBody>
</Card>
</GridItem>
</GridContainer>
);
}
}
const mapDispathToProps = (dispatch, props) => ({
removeKeys: (id) => dispatch(removeKeys(id))
});
const mapStateToProps = (state) => {
return {
keys: state.keys
}
}
export default withStyles(styles)(connect(mapStateToProps, mapDispathToProps)(KeysTable));
Dashboard
class Dashboard extends React.Component {
state = {
value: 0
};
handleChange = (event, value) => {
this.setState({ value });
};
handleChangeIndex = index => {
this.setState({ value: index });
};
render() {
const { classes } = this.props;
return (
<div>
<KeysImportForm/>
<KeysTable/>
</div>
);
}
}
Dashboard.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(dashboardStyle)(Dashboard);
I'm not 100% sure, but it looks like you are having the following error:
In your constructor you do a (unnecessary) copy of you props to your state, which introduces the error and defeats the purpose of Redux:
const data = props.keys.map((prop, key) => {
return {
id: key,
name: prop.name,
publicKey: prop.publicKey,
privateKey: prop.privateKey,
};
});
This causes your data to only update when your constructor is called, which is when your component mounts (a.k.a. you reload your page).
Instead use your props directly as your data. Redux will cause your component to re-render every time the state changes.

Getting error while showing hiding React Table columns - React JS

I am working on React Table. I am basically a beginner in React. I have a dashboard page where I display a React Table of 8 columns. I have a customize button which will open a popup page, this popup page has 8 check boxes allows me to show/hide those React columns. Initially all the check boxes in this popup page is set to true. When I uncheck a column that particular column get disabled.
There are images in the end to see what I am trying to do.
I will be using this logic for show hide columns (this question was asked by me two days back) -
How to show and hide some columns on React Table?
The React Table data is like this
const columns = [
{
Header: 'Column 1',
accessor: 'firstName',
},
{
Header: 'Column 2',
accessor: 'firstName',
},
{
Header: 'Column 3',
accessor: 'firstName',
},
{
Header: 'Column 4',
accessor: 'firstName',
},
{
Header: 'Column 5',
accessor: 'firstName',
},
{
Header: 'Column 6',
accessor: 'firstName',
},
{
Header: 'Column 7',
accessor: 'firstName'
},
{
Header: 'Column 8',
accessor: 'firstName',
}
];
The start of the dashboard page
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
filterState: {},
searchText: '',
isFilterOpen: false,
isCustomizedOpen: false,
isFiltered: false,
isSearched: false,
searchedTableData: [],
filteredTableData: [],
};
this.handleCustClickinv = this.handleCustClickinv.bind(this);
}
This is my code in the render function of my dashboard page for showing the customize button (this is written in parent dashboard page)
{this.state.isCustomizedOpen && <CustomizedView
filterState={this.state.filterState}
applyFilter={(values, clear) => { this.applyFilters(values, clear); }}
/>}
This is the code for the customize button (this is written in parent dashboard page)
<div className="custom-div-dashboard" onClick={() => { this.handleCustClickinv(); }}>
<div className='customize-view-dashboard'>Customized View </div>
</div>
This is function to handle the click on customize button (this is written in parent dashboard page)
handleFilterClickinv() {
if(this.state.isCustomizedOpen) {
this.setState({ isCustomizedOpen: false });
}
const currentState = this.state.isFilterOpen;
this.setState({ isFilterOpen: !currentState });
}
This is my entire popup page which will have 8 check boxes
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { ActionCreators } from '../../../actions';
import './enqCustomizedView.scss';
import ButtonComponent from '../../shared/button/ButtonComponent';
import { CheckBox } from '../../shared/chkbox/CheckBox';
class CustomizedView extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, value: 'Column 1', isChecked: true },
{ id: 2, value: 'Column 2', isChecked: true },
{ id: 3, value: 'Column 3', isChecked: true },
{ id: 4, value: 'Column 4', isChecked: true },
{ id: 5, value: 'Column 5', isChecked: true },
{ id: 6, value: 'Column 6', isChecked: true },
{ id: 7, value: 'Column 7', isChecked: true },
{ id: 8, value: 'Column 8', isChecked: true },
]
};
this.handleCheckChildElement = this.handleCheckChildElement.bind(this);
}
handleClick() {
this.setState({ isChecked: !this.state.isChecked });
}
handleCheckChildElement(event) {
//let items = this.state.items;
let { items } = this.state;
items.forEach(items = () => {
if(items.value === event.target.value) {
items.isChecked = event.target.checked;
}
});
this.setState({ items });
const column1checked = items[0].isChecked;
console.log('column1checked ' + column1checked);
const column2checked = items[1].isChecked;
console.log('column2checked ' + column2checked);
const column3checked = items[2].isChecked;
console.log('column3checked ' + column3checked);
const column4checked = items[3].isChecked;
console.log('column4checked ' + column4checked);
const column5checked = items[4].isChecked;
console.log('column5checked ' + column5checked);
const column6checked = items[5].isChecked;
console.log('column6checked ' + column6checked);
const column7checked = items[6].isChecked;
console.log('column7checked ' + column7checked);
const column8checked = items[7].isChecked;
console.log('column8checked ' + column8checked);
}
render() {
return (
<div className='popup-page-custom' >
<div className='bottomBar'>
<ButtonComponent
text='Apply'
className='activeButton filterMargin'
width='100'
display='inline-block'
onClick={() => { this.props.applyFilter(this.state, false); }}
/>
<ButtonComponent
text='Clear Filter'
className='greyedButton clear-filter'
width='100'
display='block'
marginTop='60'
onClick={() => { this.props.applyFilter(this.state, true); }}
/>
</div>
<div>
<div className='data-points-text'>
<span> Columns </span>
</div>
<div className="App">
<ul>
{
this.state.items.map((item, i) => {
return (<div key={i} ><CheckBox handleCheckChildElement={this.handleCheckChildElement} {...item} /></div>);
})
}
</ul>
</div>
</div>
</div>
);
}
}
CustomizedView.propTypes = {
applyFilter: PropTypes.func.isRequired
};
CustomizedView.defaultProps = {
};
function mapStateToProps(state) {
return {
auth: state.auth
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(CustomizedView);
And ultimately this is my checkbox page
import React from 'react';
import PropTypes from 'prop-types';
export const CheckBox = (props) => {
// super(props);
return (
<li>
<input key={props.id} onClick={props.handleCheckChildElement} type="checkbox" checked={props.isChecked} value={props.value} /> {props.value}
</li>
);
};
CheckBox.propTypes = {
id: PropTypes.string,
handleCheckChildElement: PropTypes.func,
isChecked: PropTypes.bool,
value: PropTypes.string,
};
CheckBox.defaultProps = {
id: '',
handleCheckChildElement: null,
isChecked: null,
value: '',
};
export default CheckBox;
This is a very basic (ugly) style of my dashboard page and popup page
This is the error I am getting on Chrome when unchecking the checkboxes
Edit 1 - As per Alireza Yadegari's suggestion, I made a 1 line change. But I am still getting 2 errors.
Edit 2 - As per Alireza Yadegari's suggestion, I applied console.
you have to use this piece of code in your constructor
this.handleCheckChildElement = this.handleCheckChildElement.bind(this)
let { items } = { ...this.state };
this is wrong ....
firstly you destructuring array to object then saying give me items prop from given object... of course this is wrong
const { items} = this.state;
takes items prop from the state
and finally.... implement your task with foreach is bad idea...
CheckBox.defaultProps = {
id: '',
handleCheckChildElement: null,
isChecked: null, value: '',
};
i don't understand what it does. you know?
I think your project is a sample and no need for further examples.
I just say about your mistakes.
first, when you are using methods it is good to use 'bind(this)' to show react where is the method belongs.
secondly, when you are using state, react just allows you to change it in the constructor and wherever you want to change it you have to use 'setState' method (you can read the reason for this in react documentation).
finally, if you have an array in your state you have to get an array in some temp object change the temp object and then apply changes with 'setState' method. if you have more question please feel free to ask.

How to create multi editable cells on click in React-table

I have a table with 4 columns: First Name, Last Name, City, Address. If the user click on City, City and Address becomes editable.
class Home extends React.PureComponent {
constructor(props){
super(props);
this.state = {
columns : [
{
Header: 'First Name',
accessor: 'firstName'
},
{
Header: 'Last Name',
accessor: 'lastName'
},
{
Header: 'City',
accessor: 'city',
Cell: this.clickableCell
},
{
Header: 'Address',
accessor: 'addr'
}
],
data: [
{
firstName: "MyName",
lastName: "MyLastName",
city: "MyCity",
addr: "My Address"
}
],
};
}
renderEditable = (cellInfo) => {
const products = this.state.data;
const row = products[cellInfo.index];
const value = row[cellInfo.column.id]
return (
<div contentEditable suppressContentEditableWarning>
{value}
</div>
)
};
clickableCell = (cellInfo) => {
const products = this.state.data;
const row = products[cellInfo.index];
const value = row[cellInfo.column.id]
const onClick = () => {
let c = Object.assign({}, this.state);
// Change the City and Address Cell's function to create an
// editable cell
c.columns[2]['Cell'] = this.renderEditable;
c.columns[3]['Cell'] = this.renderEditable;
this.setState({...c});
this.forceUpdate();
}
return (
<div onClick={onClick}>
{value}
</div>
)
};
render() {
return(
<ReactTable
className="-striped -highlight"
data={this.state.data}
columns={this.state.columns}
/>
)
}
}
My idea was bind to City's Cell a function to create a clickable div and after replace the Cell's function with a different function to render an editable div..but this idea doesn't work...

Resources