I have a React Bootstrap Table2 table functional component which gets data from a single API (at the moment) and presents the data in a table.
As part of the column definition of the table I process certain values and make them links. When a user clicks the link they will go to a page that renders the same table but with a subset of the data.
I am trying to add some logic to the component so that I can associate the various link paths with different API endpoints (e.g. if path is foo, api end point is bar, if path is foo2, api endpoint is bar2 - which then get passed to axios).
My thought was to try to use props.match.path and when there are multiple parts in the path (/section1/sub-section/sub-sub-section) break these apart and map to API endpoints.
However I cannot access props in my functional component. Is my approach the best way to do this (and if so how can I best access the data I need), or is there a better way to do this?
import BootstrapTable from "react-bootstrap-table-next";
import paginationFactory from "react-bootstrap-table2-paginator";
import React, { useState, useEffect } from "react";
import axios from "axios";
const columns = [
{
dataField: "_id",
hidden: true,
},
{
dataField: "card_id",
text: "Card Id",
sort: true,
},
{
dataField: "team",
text: "Team",
formatter: (rowContent, row) => {
console.log(row);
return (
<>
{/* {rowContent} */}
<a
target="_blank"
rel="noopener noreferrer"
// href={`${links[row.id]}`}
href={`http://www.bbc.co.uk/${rowContent}`}
// href={`http://www.bbc.co.uk/${row.card_id}`}
style={{
marginLeft: "12px",
}}
>
{rowContent}
</a>
</>
);
},
},
{
dataField: "brand",
text: "Brand",
sort: true,
},
{
dataField: "career_stage",
text: "Career Stage",
sort: true,
},
{
dataField: "forTrade",
text: "For Trade",
sort: true,
},
{
dataField: "player",
text: "Player",
formatter: (rowContent, row) => {
console.log(row);
return (
<>
{/* {rowContent} */}
<a
target="_blank"
rel="noopener noreferrer"
// href={`${links[row.id]}`}
href={`http://www.bbc.co.uk/${rowContent}`}
// href={`http://www.bbc.co.uk/${row.card_id}`}
style={{
marginLeft: "12px",
}}
>
{rowContent}
</a>
</>
);
},
},
{
dataField: "series",
text: "series",
},
/* {
dataField: "price",
text: "Price",
formatter: (cell, row) => {
return <p>${cell}</p>;
},
sort: true,
sortFunc: (a, b, order, dataField, rowA, rowB) => {
const numA = parseFloat(a);
const numB = parseFloat(b);
if (order === "asc") {
return numB - numA;
}
return numA - numB; // desc
},
}, */
];
const BasicTable = () => {
const [data, setData] = useState([]);
console.log(data);
useEffect(() => {
axios
.get(
"https://webhooks.mongodb-realm.com/api/client/v2.0/app/cards-fvyrn/service/Cards/incoming_webhook/getAllCards"
)
.then((response) => {
setData(response.data);
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
}, []);
return (
<BootstrapTable
keyField="id"
data={data}
columns={columns}
striped
hover
condensed
pagination={paginationFactory()}
/>
);
};
export default BasicTable;
Edit - OK, sorry for the stupid question. RTFM. I've posted an answer in case it's useful for other newbies who are following tutorials other than the one n the React site.
Sorry for the dumb question. If it's useful to anyone yu just need to pass in props as an argument const SingleCard = (props) => {....} then use in the component e.g. props.match.path
Related
I have created table using react that fetch data from Postgres SQL and render it.
cant understand what is the root cause for this error.
errors -
[enter image description here][1]
the table was create with an internal component.
import React, { useEffect, useState } from "react";
import { render } from "react-dom";
function ResultsFunction() {
// set state
const [results, setResults] = useState([]);
const columns = [{
id: 0, //mandatory field for arrange columns
dataField: 'test_name',
text: 'test_name',
sort: true,
visible: true, // is column visible for arrange
},{
id: 1,
dataField: 'dut_name',
text: 'name',
sort: true,
visible: true, // is column visible for arrange
}, {
id: 2,
dataField: 'dut_ip',
text: 'ip',
sort: true,
visible: true
}, {
id: 4,
dataField: 'load',
text: 'load',
sort: true,
visible: true // is column visible for arrange
}]
// first data grab
useEffect(() => {
fetch("http://12.12.12.12:9000/")
.then((resp) => resp.json())
.then((data) => {
//console.log(data.length)
//setResults(data)
setResults(mapResults(data))
});
}, []);
//console.log(results.length)
const mapResults = (results) => {
return (
results.map((result, index) => (
<li>
{results.id = {index}}
{result.test_name}
{result.dut_name}
{result.dut_ip}
{result.load}
</li>
))
);
}
console.log(columns)
console.log(results)
return (
<TableWithSearch
data={results}
columns={columns}
/>
);
}
export default ResultsFunction;
[1]: https://i.stack.imgur.com/49bU4.png
I am using react hooks and have a dynamically filled array of values after a backend call. How can I then insert these values (which I don't know a priori) in a checkbox?
I get some properties from another component.
These properties change based on a selection from a table.
I would like to be able to add / remove these properties based on a selection on a checkbox when I open the "Available roles list"
const UsersList = (props) => {
const { loadedRoles } = props;
const [checked, setChecked] = useState([]);
const selectHandler = (Event, selected) => {
loadedRoles(selected.roles);
};
const handleChange = (Event) => {
setChecked({ ...props.roles, [Event.target.name]: Event.target.checked });
};
return (
<div>
<MaterialTable
title=" Users List"
data={props.users}
columns={[
{ title: "User Id", field: "id" },
{ title: "Active", field: "active" },
{ title: "System", field: "system" },
{ title: "Roles", field: "roles" },
]}
options={{
cellStyle: {
width: 100,
minWidth: 100,
height: 1,
},
headerStyle: {
width: 100,
minWidth: 100,
},
}}
onRowClick={selectHandler}
/>
<Card>Current Role: {props.current}</Card>
<Card>
<label>Available Roles: {props.roles}</label>
<Checkbox name={props.roles} onChange={handleChange} />
</Card>
</div>
I can assume that you receive from your backend an array of objects, containing the label, keys, and values for the checkboxes?
[
{ label: 'Checkbox #1', key: 'checkbox1', checked: true },
{ label: 'Checkbox #2', key: 'checkbox2', checked: false },
{ label: 'Checkbox #3', key: 'checkbox3', checked: true },
]
You can make the call to the API in a useEffect, store the result in a local state via useState, then iterate through the values in the rendering:
const [checkboxes, setCheckboxes] = useState(null)
useEffect(() => {
fetch('/your/api')
.then(res => res.json())
.then(checkboxes => setCheckboxes(checkboxes))
}, [])
return (
<ul>
{checkboxes === null
? <li>Loading...</li>
: checkboxes.map(checkbox => (
<li key={checkbox.key}>
<label>
<input type="checkbox" name={key} defaultChecked={checkbox.checked} />
{checkbox.label}
</label>
</li>
))
}
</ul>
)
The checkboxes here are not controlled (defaultChecked). To use controlled checkboxes instead, you’ll need to create another local state and initialize it from the values returned by the backend, and use checked & onChange props instead.
In my react/redux app, i have data already in the state found in redux and i want to pass that data to my react useState in order to serve the component with the data in terms of getting the current state data or updating the state data.
I have tried but my way is not working. What is the most efficient way of going about solving this issue with react hook. Also how to configure the react useEffect properly.
// action
import { HrEmployeeData } from "../../actions/employeeHR";
const EmployeeTable = ({ employeesDetail, HrEmployeeData }) => {
const [employeesDetail, setEmployeesDetail] = React.useState([]);
useEffect(() => {
HrEmployeeData();
}, []);
const classes = useStyles();
const columns = [
{ name: "name", label: "Name" },
{ name: "phone_no", label: "Contact" },
{ name: "email", label: "Email" },
{ name: "department", label: "Department" },
{ name: "job_title", label: "Title" },
{ name: "salary", label: "Salary" },
{ name: "date_employed", label: "Date Employed" },
{
name: "Action",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<button
onClick={() =>
window.alert(`Clicked "Edit" for row ${tableMeta.rowIndex}`)
}
>
Edit
</button>
);
},
},
},
];
return (
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper className={classes.paper}>
<MUIDataTable
title={"Employees Records"}
data={employeesDetail} <--- WHERE THE DATA IS BEEING USED
columns={columns}
options={options}
/>
</Paper>
</Grid>
</Grid>
</div>
);
};
EmployeeTable.propTypes = {
employeesDetail: PropTypes.object,
};
const mapStateToProps = (state) => ({
employeesDetail: state.employeeHR.employeesDetail,
});
export default connect(mapStateToProps, { HrEmployeeData })(EmployeeTable);
You don't have to pass the data from the redux store the state.
The mechanism to use the redux store in components is "props".
In this case you're already mapping store to props and you can use it in the components.
Just remove the useState line.
I'm new on React and this is my first page that I created, I'm using material-table and material-ui to help me.
This is my code:
import React, { useState, useEffect } from 'react';
import { Fade } from "#material-ui/core";
import MaterialTable from 'material-table';
import { makeStyles } from '#material-ui/core/styles';
import api from '../../services/api.js';
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
width: '70%',
margin: 'auto',
marginTop: 20,
boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.4)'
}
}));
function User(props) {
const classes = useStyles();
const [checked, setChecked] = useState(false);
const [tableData, setTableData] = useState([]);
let config = {
columns: [
{ title: 'Name', field: 'name' },
{ title: 'Sector', field: 'sector' },
{ title: 'E-mail', field: 'email'},
{ title: 'Tel', field: 'tel'}
],
actions: [
{ icon: 'create', tooltip: 'Edit', onClick: (event, rowData) => alert('Edit')},
{ icon: 'lock', tooltip: 'Block', onClick: (event, rowData) => alert('Block')},
{ icon: 'delete', tooltip: 'Delete', onClick: (event, rowData) => alert('Delete') },
{ icon: 'visibility', tooltip: 'Last Access', onClick: (event, rowData) => alert('Last') },
{ icon: "add_box", tooltip: "Add", position: "toolbar", onClick: () => { alert('Add') } }
],
options: {
headerStyle: { color: 'rgba(0, 0, 0, 0.54)' },
actionsColumnIndex: -1,
exportButton: true,
paging: true,
pageSize: 10,
pageSizeOptions: [],
paginationType: 'normal'
},
localization: {
body: {
emptyDataSourceMessage: 'No data'
},
toolbar: {
searchTooltip: 'Search',
searchPlaceholder: 'Search',
exportTitle: 'Export'
},
pagination: {
labelRowsSelect: 'Lines',
labelDisplayedRows: '{from} to {to} {count} itens',
firstTooltip: 'First',
previousTooltip: 'Previous',
nextTooltip: 'Next',
lastTooltip: 'Last'
},
header: {
actions: 'Actions'
}
}
}
useEffect(() => {
setChecked(prev => !prev);
async function loadUsers() {
const response = await api.get('/users');
setTableData(response.data);
}
loadUsers();
}, [])
return (
<>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<Fade in={checked} style={{ transitionDelay: checked ? '300ms' : '0ms' }}>
<div className={classes.root}>
<MaterialTable editable={config.editable} options={config.options} localization={config.localization} title="Usuários" columns={config.columns} data={tableData} actions={config.actions}></MaterialTable>
</div>
</Fade>
</>
);
}
export default User;
This code works, and list the users that I have on my database. My next step is create a Popover on actions Block and Delete to show to user a question to confirm if he really wanna delete or block the user on the line.
I have searched in documentation of material-ui (https://material-ui.com/components/popover/) how can I use the Popover with my table, but I cannot do this. I think I cannot use this prop aria-describedby={id}, but I don't know.
Someone can help me to achieve this or help to find a way?
I think you are not looking for a popover which is more like a small tooltip, but for a dialog, which needs attention from the user and blocks him from doing anything else.
Material-UI has a dialog component. You place it somewhere in the code. If the property "open" is true it blocks the user from doing anything else but confirming/canceling the deletion. If it set to false the dialog is not visible.
https://material-ui.com/components/dialogs/
I have a table which displays clinics. I have also a onPageChange prop which handles the pageIndex and then i fetch the data based on that page. Below is my table configuration
import 'react-table/react-table.css'
import React, { Component } from 'react';
import ClinicFormComponent from './newClinicForm';
import SearchFormComponent from '../search/searchForm';
import { connect } from 'react-redux';
import { fetchClinics, fetchClinic, deleteClinic, searchClinics, pushBreadcrumb, popBreadcrumb } from '../../actions/index.js';
import { toast } from 'react-toastify';
import ReactTable from 'react-table'
import store from '../../helpers/store';
import { ic_search } from 'react-icons-kit/md/ic_search';
import SvgIcon from 'react-icons-kit';
require('normalize.css/normalize.css');
// require('styles/App.css');
class ClinicsPage extends Component {
constructor() {
super();
this.handleClickForm = this.handleClickForm.bind(this);
this.handleClickFormSearch = this.handleClickFormSearch.bind(this);
this.closeForm = this.closeForm.bind(this);
this.onPageChange = this.onPageChange.bind(this);
}
componentDidMount(){
this.props.searchClinics({ country: 'Australia' });
}
onPageChange(pageIndex) {
this.props.fetchClinics(pageIndex, null);
}
render() {
let { devs } = this.props;
const columns = [{
Header: 'Id',
accessor: 'id' // String-based value accessors!
}, {
Header: 'Name',
accessor: 'name'
}, {
Header: 'Description', // Required because our accessor is not a string
accessor: 'description',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Country', // Required because our accessor is not a string
accessor: 'country',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Area', // Required because our accessor is not a string
accessor: 'area',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Latitude', // Required because our accessor is not a string
accessor: 'latitude',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Longitude', // Required because our accessor is not a string
accessor: 'longitude',
sortable: true
// accessor: d => d.friend.name // Custom value accessors!
},
{
Header: 'Tags', // Required because our accessor is not a string
accessor: 'tags',
sortable: true,
Cell: (row) => {if (row.original.tags.length>1) { return row.original.tags.join(', ') } else { return row.original.tags } }
},
{
Header: () => <span className="text-center">Actions</span>,
accessor: 'id',
id: 'actions',
sortable: true,
Cell: (row) => (<span><button className="text-center btn btn-primary-zoetis margin_right_5" onClick={()=>{this.props.history.push('/editClinic/'+ row.original.id)}}>Edit</button ><button className="text-center btn btn-danger" onClick={()=>{this.props.deleteClinic(row.original.id, row.original)}}>Delete</button ></span>)
}
]
return (
<div className="wrap">
<div className="row margin_top_10 margin_bottom_5">
<div className="col-sm-6">
<a className="btn btn-link color_zoetis btn_zoetis_alt" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="collapseExample2" onClick={this.handleClickForm}>NEW CLINIC</a>
</div>
<div className="col-sm-6">
<a className="nav-link float_right search-clinic-btn" data-toggle="collapse" onClick={this.handleClickFormSearch} role="button" aria-expanded="false" aria-controls="collapseExample"><span className="search_label">Search data entries...</span> <SvgIcon size={25} icon={ic_search}/></a>
</div>
</div>
<div id="nav-tabContent">
<ReactTable
data={devs.clinics}
pageSizeOptions= {[10]}
defaultPageSize= {10}
columns={columns}
pages={devs.paginationData.totalPages || ''}
sortable={true}
multiSort={true}
//manual
filterable
page={devs.paginationData.pageNumber}
loading={devs.isFetching}
onPageChange={this.onPageChange}
noDataText='No Data Found'
className='-striped -highlight'
/>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
devs: state.reducer.devs,
showMenu: state.reducer.devs.showMenu,
showEditMenu: state.reducer.devs.showEditMenu,
paginationData: state.reducer.devs.paginationData,
location: state.router.location.pathname
}
}
export const mapDispatchToProps = {
fetchClinics,
searchClinics,
fetchClinic,
deleteClinic,
pushBreadcrumb,
popBreadcrumb
}
export default connect(mapStateToProps, mapDispatchToProps)(ClinicsPage);
Notice that i have disabled manual prop. If i enable the manual prop then i can navigate through next and previous pages but i cannot sort or filter the data.
With the manual prop disabled the filtering and the sorting works correct but when i navigate in the next page the table is showing empty. The first page displays correct the first 10 data. Also i have tested the api and returns correct the next 10 data.
Is there any workaround? To keep both server side pagination and alse the default sorting and filtering?
Test decomposing manual property
<ReactTable
filtered={this.state.filtered}
onFilteredChange={this.onFilteredChange.bind(this)}
defaultFilterMethod={(filter, row) =>
String(row[filter.id]) === filter.value
}
columns={columns}
ref={r => (this.selectTable = r)}
className="-striped -highlight"
defaultPageSize={10}
data={this.state.data}
pages={this.state.pages}
loading={this.state.loading}
manual <-------
resizable={true}
filterable
filterAll={true}
onFetchData={(state, instance) => {
this.setState({loading: true})
axios.get(`${API}${controller}`, {
params: {
pages: state.page,
pageSize: state.pageSize,
filtered: state.filtered,
typeOption: `${type}`,
dateinit: `${this.state.dateinit}`,
datefinal: `${this.state.datefinal}`,
data: this.props.data
},
headers: { 'Authorization': `${tokenCopyPaste()}` }
})
.then((res) => {
this.props.exportTable(res.data, type);
this.setState({
data: res.data.data,
fulldata: res.data,
pages: Math.ceil(res.data.pages / state.pageSize),
loading: false
})
})
}
}
/>