React beginner here, i'm currently learning Reactjs, since everybody is saying React hooks is easier to start with, so i started with hooks, but everywhere is mainly react with classes, here in this example (antd table) it is coded using classes, if i have understood it currently, i should put 'searchText:'',searchedColumn:'',' into useState, and dont need useEffect ? since there is no componentdimount or udpate .. ? just for learning want to change this class code into Hooks, sory for mistakes because english is not my mother language:
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Table, Input, Button, Space } from 'antd';
import Highlighter from 'react-highlight-words';
import { SearchOutlined } from '#ant-design/icons';
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Joe Black',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Jim Green',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
class App extends React.Component {
state = {
searchText: '',
searchedColumn: '',
};
getColumnSearchProps = dataIndex => ({
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
ref={node => {
this.searchInput = node;
}}
placeholder={`Search ${dataIndex}`}
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
style={{ width: 188, marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
icon={<SearchOutlined />}
size="small"
style={{ width: 90 }}
>
Search
</Button>
<Button onClick={() => this.handleReset(clearFilters)} size="small" style={{ width: 90 }}>
Reset
</Button>
<Button
type="link"
size="small"
onClick={() => {
confirm({ closeDropdown: false });
this.setState({
searchText: selectedKeys[0],
searchedColumn: dataIndex,
});
}}
>
Filter
</Button>
</Space>
</div>
),
filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
onFilter: (value, record) =>
record[dataIndex]
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
: '',
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => this.searchInput.select(), 100);
}
},
render: text =>
this.state.searchedColumn === dataIndex ? (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[this.state.searchText]}
autoEscape
textToHighlight={text ? text.toString() : ''}
/>
) : (
text
),
});
handleSearch = (selectedKeys, confirm, dataIndex) => {
confirm();
this.setState({
searchText: selectedKeys[0],
searchedColumn: dataIndex,
});
};
handleReset = clearFilters => {
clearFilters();
this.setState({ searchText: '' });
};
render() {
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: '30%',
...this.getColumnSearchProps('name'),
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '20%',
...this.getColumnSearchProps('age'),
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
...this.getColumnSearchProps('address'),
},
];
return <Table columns={columns} dataSource={data} />;
}
}
ReactDOM.render(<App />, document.getElementById('container'));
Code can also be found here: https://codesandbox.io/s/lq2it?file=/package.json
Yes, searchText and searchedColumn are states and should be declared with useState like so:
const [searchText, updateSearchText] = useState('default_state');
const [searchedColumn, updateSearchedColumn] = useState('default_state');
updateSearchText(some_value) is the equivalent for
this.setState({
searchText : some_value
})
and updateSearchedColumn is the same said above but for the state: searchedColumn
Related
I have a table that renders two buttons, delete and edit row.
On both of them I need to access the row Id.
I tried to use customBodyRender but it did not work, i have only the dataIndex and the rowIndex, but what I need is the actual row object value.
Updated question with the code
const columns = [
{
name: "id",
label: "Id",
options: {
display: false
}
},
{
name: "name",
label: "Name",
},
{
name: "Actions",
options: {
filter: false,
sort: false,
empty: true,
customBodyRender: (dataIndex, rowIndex) => {
return (
<>
<IconButton aria-label="edit" onClick={() => {
alert(dataIndex + " - " + rowIndex)
}}>
<EditIcon />
</IconButton>
<IconButton color="primary" aria-label="delete" style={{ marginLeft: "10px" }} onClick={() => {
alert(dataIndex)
}}>
<DeleteIcon />
</IconButton>
</>
);
}
}
}];
This is how MUIDataTable is being used
<MUIDataTable
title={"Lista de Turnos"}
data={shifts}
columns={columns}
options={{
selectableRowsHideCheckboxes: true,
textLabels: {
body: {
noMatch: 'Não foram encontrados registros para serem mostrados',
},
},
}}
/>
You can use customBodyRenderLite instead of customBodyRender
The actual code would be like this if you want to access the actual data object.
import React from "react";
import ReactDOM from "react-dom";
import MUIDataTable from "mui-datatables";
import Button from '#material-ui/core/Button'
function App() {
const data = [
{id:1,name:'wahid'},
{id:2,name:'jamil'},
{id:3,name:'marin'},
];
const columns = [
{
name: "id",
label: "Id",
options: {
display: false
}
},
{
name: "name",
label: "Name",
},
{
name: "Actions",
options: {
filter: false,
sort: false,
customBodyRenderLite: (dataIndex, rowIndex) => {
return (
<Button aria-label="edit" onClick={() => {
alert(data[dataIndex].name)
}}>
Button
</Button>
);
}
},
}
];
return (
<React.Fragment>
<MUIDataTable
title={"ACME Employee list"}
data={data}
columns={columns}
options={{
selectableRowsHideCheckboxes: true,
textLabels: {
body: {
noMatch: 'Não foram encontrados registros para serem mostrados',
},
},
}}
/>
</React.Fragment>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
I tried to the material-table the library for basic crud operation. By using onRowAdd, onRowUpdate, onRowDelete, I get the icons for the same but I would like to know that how can I change the color of each of these three icons?
You can see my table has few icons and I am focusing on add, edit, delete icons I want to change color of these icons.
Here is the link to my codesandbox.
App.js file
import React, { useState } from 'react';
import './App.css';
import MaterialTable from 'material-table'
const empList = [
{ id: 1, name: "Neeraj", email: 'neeraj#gmail.com', phone: 9876543210, city: "Bangalore" },
{ id: 2, name: "Raj", email: 'raj#gmail.com', phone: 9812345678, city: "Chennai" },
{ id: 3, name: "David", email: 'david342#gmail.com', phone: 7896536289, city: "Jaipur" },
{ id: 4, name: "Vikas", email: 'vikas75#gmail.com', phone: 9087654321, city: "Hyderabad" },
]
function App() {
const [data, setData] = useState(empList)
const columns = [
{ title: "ID", field: "id", editable: false },
{ title: "Name", field: "name" },
{ title: "Email", field: "email" },
{ title: "Phone Number", field: 'phone', },
{ title: "City", field: "city", }
]
return (
<div className="App">
<h1 align="center">React-App</h1>
<h4 align='center'>Material Table with CRUD operation</h4>
<MaterialTable
title="Employee Data"
data={data}
columns={columns}
editable={{
onRowAdd: (newRow) => new Promise((resolve, reject) => {
const updatedRows = [...data, { id: Math.floor(Math.random() * 100), ...newRow }]
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowDelete: selectedRow => new Promise((resolve, reject) => {
const index = selectedRow.tableData.id;
const updatedRows = [...data]
updatedRows.splice(index, 1)
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowUpdate:(updatedRow,oldRow)=>new Promise((resolve,reject)=>{
const index=oldRow.tableData.id;
const updatedRows=[...data]
updatedRows[index]=updatedRow
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
})
}}
options={{
actionsColumnIndex: -1, addRowPosition: "first"
}}
/>
</div>
);
}
export default App;
You can override the icons and provide custom styles by setting the icons props. It accepts an object where the key is a type of operation (Add, Edit, Delete,...) and the value is an icon component. For reference, see the all-props section here.
<MaterialTable
{...props}
icons={{
Edit: () => <EditIcon style={{ color: "orange" }} />,
Delete: () => <DeleteIcon style={{ color: "red" }} />
}}
>
Live Demo
It's Simple. Inspect on the page and Select the Icon and Copy its style Name in Styles Tab.
Now, Go to App.css file and Create New Style with the icon style name shown on Inspect-styles area and there you can enter your desired color.
It will work.
In your App.css File,
Add below code
.MuiIconButton-colorInherit {
color: red;
}
change to any color
I have a list of li elements that is being passed down to one of my components. These are being rendered in the component. I have a search bar in the same component. I want to be able to only render the items that match what is written down in the search bar. This is what my component looks like.
import React, {Component} from 'react'
import {NavLink} from 'react-router-dom'
import LikeBtn from './LikeBtn'
class SearchForm extends Component {
constructor(props) {
super(props)
this.state = {
search: '',
newList: []
}
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
})
}
render() {
let list = this.props.listProp.map(item => <li className="listItem" key={item.id}><NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>{item.name}</NavLink><LikeBtn /></li>)
let newList = list.filter(item => item.innerText === this.state.search)
console.log(newList)
return (
<>
<input type="text" name='search' onChange={this.handleChange}/>
<ul>
{list}
</ul>
</>
)
}
}
export default SearchForm
I don't know how to get that filtered out so that I can render the items. I tried doing innerText but since I have a LikeBtn component in the li element my filter doesn't work. How else would I be able to implement this? Are there more efficient ways of doing this?
You need to filter your data and not grab something you've already rendered on screen.
render() {
let filteredList = this.state.search
? this.props.listProp.filter((item) =>
item.name.includes(this.state.search),
)
: this.props.listProp;
return (
<>
<input type="text" name="search" onChange={this.handleChange} />
<ul>
{filteredList.map((item) => (
<li className="listItem" key={item.id}>
<NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>
{item.name}
</NavLink>
<LikeBtn />
</li>
))}
</ul>
</>
);
}
There is also a snippet below that you can run:
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {
search: '',
};
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
render() {
let filteredList = this.state.search
? this.props.listProp.filter((item) =>
item.name.toLowerCase().includes(this.state.search.toLowerCase())
)
: this.props.listProp;
return (
<React.Fragment>
<input type="search" name="search" autocomplete="off" onChange={this.handleChange} />
<ul>
{filteredList.map((item) => (
<li className="listItem" key={item.id}>
<a href={`activities/${item.id}`}>{item.name}</a>
<button>Like</button>
</li>
))}
</ul>
</React.Fragment>
);
}
}
const data = [
{
id: 1,
name: 'Congress, The',
},
{
id: 2,
name: 'Glen or Glenda',
},
{
id: 3,
name: "Don't Drink the Water",
},
{
id: 4,
name: 'Blind',
},
{
id: 5,
name: 'Sirocco',
},
{
id: 6,
name: 'Sunset Strip',
},
{
id: 7,
name: 'Better Living',
},
{
id: 8,
name: '4:44 Last Day on Earth',
},
{
id: 9,
name: 'The Heavy',
},
{
id: 10,
name: 'Dr. Who and the Daleks',
},
{
id: 11,
name: 'Legend of Hell House, The',
},
{
id: 12,
name: 'Exit Humanity',
},
{
id: 13,
name: 'Young in Heart, The',
},
{
id: 14,
name: 'Soul Kitchen',
},
{
id: 15,
name: 'Accattone',
},
];
ReactDOM.render(<SearchForm listProp={data} />, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can move this part
let list = this.props.listProp.map(item => <li className="listItem" key={item.id}><NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>{item.name}</NavLink><LikeBtn /></li>)
to the return and filter the listProp on handleChange.
You can refer to this codeSandbox here for a working sample
I have this component which is a filter for a table..
handleSearch function is responsible to update const filters... its work perfectly when dataIndex props is the same, but when it changes, filters value is backing to it's initial value, an empty array.
I can't manage to resolve it, I've already console log everything.
import React, { useState, } from "react";
import { SearchOutlined } from "#ant-design/icons";
import { Select, Button, Space } from "antd";
const TableFilter = (props) => {
const {
filterType,
filterMode,
filterOptions,
FilterSelectOnFocus,
dataIndex,
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
} = props;
const [filters, setFilters] = useState([]);
const SelectFilter = (
<Select
style={{ width: 188, marginBottom: 8, display: "block" }}
type={filterType}
mode={filterMode}
name={dataIndex}
value={selectedKeys}
optionFilterProp="children"
placeholder={`Search ${dataIndex}`}
onFocus={FilterSelectOnFocus}
showSearch
onChange={(value) => setSelectedKeys(value ? value : [])}
getPopupContainer={(trigger) => trigger}
notFoundContent
>
{filterOptions?.map((type, key) => (
<Select.Option value={type.value} key={key}>
{type.label}
</Select.Option>
))}
</Select>
);
const defaultFilterTypes = [
{
type: "select",
element: SelectFilter,
},
];
const handleFilterType = () => {
const type = defaultFilterTypes.find((types) => types.type === filterType);
return type.element;
};
const handleSearch = () => {
console.log(filters) //is empty when dataIndex value change, when it's is the same it get the update value of the 75 line
confirm();
const newFilterValues = [...filters]
const index = newFilterValues.findIndex(newValue => newValue.searchedColumn === dataIndex)
if(index === -1){
newFilterValues.push({ searchText: selectedKeys, searchedColumn: dataIndex})
}
else{
newFilterValues[index] = {searchText: selectedKeys, searchedColumn: dataIndex}
}
setFilters(newFilterValues)
}
const handleReset = () => {
console.log('reset');
clearFilters();
setFilters({ searchText: "" });
setSelectedKeys([]);
};
return (
<div style={{ padding: 8 }}>
{handleFilterType()}
<Space>
<Button
type="primary"
onClick={() => handleSearch()}
icon={<SearchOutlined />}
size="small"
style={{ width: 90 }}
>
Search
</Button>
<Button
onClick={() => handleReset()}
size="small"
style={{ width: 90 }}
>
Reset
</Button>
</Space>
</div>
);
};
export default TableFilter;
Table Component
import React, { useEffect, useState } from "react";
import { Table } from "antd";
import { getTransactions } from "../../../../api/Transactions";
import { formatCnpjCpf, formatCurrency } from "../../../../utils/masks";
import TableFilter from "../../../../shared-components/ant-design/containers/TableFilters";
import { getPartnersAsOptions } from "../../../../api/Partners";
const Insider = (props) => {
const [data, setData] = useState([]);
const [paginationValues, setPaginationValues] = useState({
current: 1,
pageSize: 50,
total: 0,
position: ["topRight"],
});
const [partners, setPartners] = useState([{value: null, label: 'carregando...'}])
const context = "insider";
function getColumnSearchProps(
dataIndex,
filterType,
filterMode,
filterOptions,
FilterSelectOnFocus
) {
return {
filterDropdown: ({
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
}) => {
return (
<TableFilter
dataIndex={dataIndex}
filterType={filterType}
filterMode={filterMode}
filterOptions={filterOptions}
FilterSelectOnFocus={FilterSelectOnFocus}
setSelectedKeys={setSelectedKeys}
selectedKeys={selectedKeys}
confirm={confirm}
clearFilters={clearFilters}
/>
);
},
};
}
async function getPartners(){
if(partners.length > 2){
return
}
const response = await getPartnersAsOptions(paginationValues)
setPartners(response.data)
}
const columns = [
{
dataIndex: ["transactionType", "desc"],
title: "Tipo de Transação",
sorter: true,
key: "orderTransactionType",
...getColumnSearchProps("orderTransactionType"),
},
{
dataIndex: "transactionDate",
title: "Data Transação",
key: "orderTransactionDate",
sorter: true,
...getColumnSearchProps("orderTransactionDate"),
},
{
title: "Nome origem",
dataIndex: ["source", "name"],
sorter: true,
key: "orderSourceCustomerName",
},
{
render: (render) => formatCnpjCpf(render.source.document.value),
title: "Documento origem",
key: "sourceCustomer",
...getColumnSearchProps("sourceCustomer", "select", "tags")
},
{
title: "Nome destino",
dataIndex: ["target", "name"],
sorter: true,
key: "orderTargetCustomerName",
},
{
render: (render) => formatCnpjCpf(render.target.document.value),
title: "Documento destino",
},
{
render: (render) => formatCurrency(render.value),
title: "Valor da transação",
key: "orderValue",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.chargedTariff),
title: "Tarifa",
key: "orderChargedTariff",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.cost),
title: "Custo",
key: "orderCost",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.revenue),
title: "Receita",
key: "orderRevenue",
sorter: true,
align: "right",
},
{
title: "Parceiro",
name: "Parceiro",
dataIndex: ["partner", "name"],
key: "orderPartnerName",
sorter: true,
align: "center",
...getColumnSearchProps(
"orderPartnerName",
"select",
"multiple",
partners,
getPartners)
},
{
title: "id da transação",
name: "id da transação",
dataIndex: "id",
},
];
useEffect(function transactions() {
async function fetchTransactions() {
const response = await getTransactions(context, paginationValues);
if (response) {
const { data, pagination } = response;
setData(data);
setPaginationValues(pagination);
}
}
fetchTransactions();
// eslint-disable-next-line
}, []);
return <Table dataSource={data} columns={columns} />;
};
export default Insider;
You could move this piece of code
const [filters, setFilters] = useState([]);
In a higher level
I am trying to identify a specific panel in an array when it is expanded and be able to connect that panel's id to the button, as well as disable the button if no panel is expanded or more than 1 panel is expanded. For whatever reason, it's not taking in the id at all. Also, I am having problems with how to disable the button correctly.
export default class WorkoutList extends Component {
constructor(props) {
super(props);
this.state = {
workoutlist: [
{
id: uuid.v4(),
name: 'Leg Day',
date: '08/09/19',
duration: 60,
exerciselist: [
{
id: uuid.v4(),
exerciseName: 'Squats',
numberOfSets: 3,
reps: 12,
weight: 135,
},
{
id: uuid.v4(),
exerciseName: 'Leg press',
numberOfSets: 3,
reps: 10,
weight: 150,
},
{
id: uuid.v4(),
exerciseName: 'Lunges',
numberOfSets: 4,
reps: 12,
},
],
selected: false,
},
{
id: uuid.v4(),
name: 'Arm Day',
date: '08/10/19',
duration: 90,
exerciselist: [
{
id: uuid.v4(),
exerciseName: 'Bench Press',
numberOfSets: 5,
reps: 5,
weight: 225,
},
{
id: uuid.v4(),
exerciseName: 'Chest Flies',
numberOfSets: 3,
reps: 10,
weight: 50,
},
{
id: uuid.v4(),
exerciseName: 'Tricep Extensions',
numberOfSets: 4,
reps: 12,
weight: 70,
},
],
selected: false,
},
{
id: uuid.v4(),
name: 'Running',
date: '08/11/19',
duration: 40,
exerciselist: [],
selected: false,
},
],
disabled: false
}
this.handleSelectedPanel = this.handleSelectedPanel.bind(this);
this.handleButton = this.handleButton.bind(this);
}
handleSelectedPanel(id) {
const { workoutlist } = this.state;
this.setState({
workoutlist: workoutlist.map(workout => {
if (workout.id === id) {
workout.selected = !workout.selected
}
return workout;
})
})
}
handleButton(){
const { workoutlist, disabled } = this.state;
let count = 0;
workoutlist.map((workout) => {
if(workout.selected === true) {
count = count + 1;
}
return count;
})
if (count !== 1) {
this.setState({
disabled: true
})
} else {
this.setState({
disabled: false
})
}
return disabled;
}
render() {
const { workoutlist } = this.state;
return (
<div>
<CssBaseline />
<ClientMenuBar title="My Workouts" />
<div style={styles.workoutlist}>
<Paper style={styles.paper} elevation={0}>
{workoutlist.map((workout) => (
<WorkoutItem
key={workout.id}
workout={workout}
onSelectedPanel={this.handleSelectedPanel}
/>
))}
</Paper>
<Button
variant="contained"
color="primary"
size="small"
style={styles.button}
disabled={this.handleButton}
>
Start Workout
</Button>
</div>
</div>
)
}
}
export default class WorkoutItem extends Component {
constructor(props){
super(props);
this.handleSelectedPanel = this.handleSelectedPanel.bind(this);
}
handleSelectedPanel(e) {
this.props.onSelectedPanel(e.target.id);
}
render() {
const { id, name, date, duration, exerciselist } = this.props.workout;
return (
<ExpansionPanel style={styles.panel} id={id} onChange={this.handleSelectedPanel}>
<ExpansionPanelSummary>
<Typography variant="button" style={{ width: "33%" }}>
{name}
</Typography>
<Typography variant="button" style={{ width: "33%" }}>
({date})
</Typography>
<Typography align="right" style={{ width: "33%" }}>
~{duration} mins
</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Table size="medium" style={styles.table}>
<TableHead>
<TableRow>
<TableCell padding="none">Name</TableCell>
<TableCell padding="none" align="right"># of sets</TableCell>
<TableCell padding="none" align="right">reps</TableCell>
<TableCell padding="none" align="right">weight</TableCell>
</TableRow>
</TableHead>
<TableBody>
{exerciselist.map((exercise) => (
<ExerciseList
key={exercise.id}
exercise={exercise}
/>
))}
</TableBody>
</Table>
<ExpansionPanelActions disableSpacing style={styles.actionButton}>
<Button color="primary" size="small" disableRipple>
edit
</Button>
</ExpansionPanelActions>
</ExpansionPanelDetails>
</ExpansionPanel>
)
}
}
It doesn't seem to be taking in the id at all, and when i try to disable the button, it throws this error:
Warning: Failed prop type: Invalid prop disabled of type function supplied to ForwardRef(Button), expected boolean.
The warning you are seeing comes from:
<Button
variant="contained"
color="primary"
size="small"
style={styles.button}
disabled={this.handleButton}
>
In the error it says a function is passed to disabled which should be a boolean, so change the prop that disabled takes to be that boolean (rather than the function this.handleButton).
e.target.id doesn't have what you actually want in there (it actually probably isn't a thing). You can use e.target.value to get a value out of something like an input where you want to get something information from the DOM node you are working with but in this case the information isn't something entered and actually something that the component already has in its scope (in the props). So instead of:
handleSelectedPanel(e) {
this.props.onSelectedPanel(e.target.id);
}
do this
handleSelectedPanel(e) {
this.props.onSelectedPanel(this.props.workout.id);
}