How to update a particular object in array of objects. For example,
have an object like this
tabs: [{
name: "Step 1",
DroppedDetails: [
{id:1,name:Step1,
DroppedDetails:[
{DraggedItemRandomId: "70d19a9f-7e6e-4eb2-b974-03e3a8f03f08"
draggedItem:
category: "basic"
disabled: false
fieldClass: "Text"
height: 30
id: "text"
image: "/static/media/type.327c33c2.png"
label: "Text Field"
placeholder: "Edit This Field"
readOnly: false
required: false
width: 200
}
{DraggedItemRandomId: "70d19a9f-7e6e-4eb2-b974-039e3a8f03f0"
draggedItem:
category: "basic"
disabled: false
fieldClass: "Text"
height: 30
id: "text"
image: "/static/media/type.327c33c2.png"
label: "Text Field"
placeholder: "Edit This Field"
readOnly: false
required: false
width: 200
}
]
},
{
name: "Step 2",
DroppedDetails: [
{DraggedItemRandomId: "70d19a39-7e6e-4eb2-b974-03e3a82f03f0"
draggedItem:
category: "basic"
disabled: false
fieldClass: "Text"
height: 30
id: "text"
image: "/static/media/type.327c33c2.png"
label: "Text Field"
placeholder: "Edit This Field"
readOnly: false
required: false
width: 200
}]
}
],
and my new value should update is
{
DraggedItemRandomId: "70d19a9f-739e-4eb2-b974-03e3a8f032d1",
draggedItem:{
category: "basic"
disabled: false
fieldClass: "Text"
height: 30
id: "text"
image: "/static/media/type.327c33c2.png"
label: "Text Field"
placeholder: "Hey Sagar" // updated value
readOnly: true //updated value
required: true //updated value
width: 200}
}
}
How can i Update this object in state (0th or 1st object dynamically)
object like
how can i do setState for inner loop of array of objects dynamically???
i have tried so many examples but no result ..please help me guyz
final output:
tabs: [{
name: "Step 1",
DroppedDetails: [
{id:1,name:Step1,
DroppedDetails:[
{DraggedItemRandomId: "70d19a9f-7e6e-4eb2-b974-03e3a8f03f08"
draggedItem:
category: "basic"
disabled: false
fieldClass: "Text"
height: 30
id: "text"
image: "/static/media/type.327c33c2.png"
label: "Text Field"
placeholder: "Hey Sagar" // updated value
readOnly: true //updated value
required: true //updated value
width: 200
}
{DraggedItemRandomId: "70d19a9f-7e6e-4eb2-b974-03e3a8f03f08"
draggedItem:
category: "basic"
disabled: false
fieldClass: "Text"
height: 30
id: "text"
image: "/static/media/type.327c33c2.png"
label: "Text Field"
placeholder: "Edit This Field"
readOnly: false
required: false
width: 200
}
]
},
{
name: "Step 2",
DroppedDetails: [
{DraggedItemRandomId: "70d19a9f-7e6e-4eb2-b974-03e3a8f03f08"
draggedItem:
category: "basic"
disabled: false
fieldClass: "Text"
height: 30
id: "text"
image: "/static/media/type.327c33c2.png"
label: "Text Field"
placeholder: "Edit This Field"
readOnly: false
required: false
width: 200
}]
}
],
You can get, first of all, the tabs of the state:
const { tabs } = this.state;
// here you code to decide what tab do you want to update
const tabSelected = tabs[0];
const { DroppedDetails } = tabSelected;
DroppedDetails[0]= {
name: "sagar111"
};
// You can edit another one or add a new one to the array also.
DroppedDetails[1]= {
name: "NameEdited"
};
DroppedDetails.push({ name: "New Name" })
And set state a new state:
this.setState(
{ tabs: tabs.map(t => t === tabSelected ? { ...tabSelected, DroppedDetails }) : t });
But it could be in this way too:
this.setState(tabs);
Because the original references were updated.
At the end DroppedDetails and tabs[0].DroppedDetails[0] are the same object.
how can i do setState for inner loop of array of objects???
It's not recommended the use of setState in a forloop.
Check this Calling setState in a loop only updates state 1 time
You can easily do that (this changes the name to lower case):
const { tabs } = this.state;
tabs.map(tab => {
// Do something with your tab value like the following
const newDroppedDetails = tab.map(({ name }) => ({ name: name.toLowerCase() });
return {
...tab,
DroppedDetails: newDroppedDetails
}
});
The key point here is to not mutate objects or arrays, but to create a new reference to objects that you modify, so the shallow compare on PureComponents will always work properly.
Applying the same concepts from your original codesandbox, we can do something like this to edit each individual Dropdown Detail.
working sandbox https://codesandbox.io/s/tab-creator-v2-8hc7c
import React from "react";
import ReactDOM from "react-dom";
import uuid from "uuid";
import "./styles.css";
class App extends React.Component {
state = {
tabs: [
{
id: 1,
name: "Tab 1",
content: "Wow this is tab 1",
DroppedDetails: [
{ name: "Bhanu", editing: false },
{ name: "Sagar", editing: false }
]
},
{
id: 2,
name: "Tab 2",
content: "Look at me, it's Tab 2",
DroppedDetails: [
{ name: "Christopher", editing: false },
{ name: "Ngo", editing: false }
]
}
],
currentTab: {
id: 1,
name: "Tab 1",
content: "Wow this is tab 1",
DroppedDetails: [
{ name: "Bhanu", editing: false },
{ name: "Sagar", editing: false }
]
},
editMode: false,
editTabNameMode: false
};
handleDoubleClick = () => {
this.setState({
editTabNameMode: true
});
};
handleEditTabName = e => {
const { currentTab, tabs } = this.state;
const updatedTabs = tabs.map(tab => {
if (tab.id === currentTab.id) {
return {
...tab,
name: e.target.value
};
} else {
return tab;
}
});
this.setState({
tabs: updatedTabs,
currentTab: {
...currentTab,
name: e.target.value
}
});
};
handleOnBlur = () => {
this.setState({
editTabNameMode: false
});
};
handleDetailChange = (e, id, index) => {
const { tabs, currentTab } = this.state;
const updatedCurrentTab = { ...currentTab };
updatedCurrentTab.DroppedDetails = updatedCurrentTab.DroppedDetails.map(
(detail, detailIndex) => {
if (index == detailIndex) {
return {
...detail,
name: e.target.value
};
} else {
return detail;
}
}
);
const updatedTabs = tabs.map(tab => {
if (tab.id == id) {
return {
...tab,
DroppedDetails: tab.DroppedDetails.map((detail, detailIndex) => {
if (detailIndex == index) {
return {
...detail,
name: e.target.value
};
} else {
return detail;
}
})
};
} else {
return tab;
}
});
this.setState({
tabs: updatedTabs,
currentTab: updatedCurrentTab
});
};
createTabs = () => {
const { tabs, currentTab, editTabNameMode } = this.state;
const allTabs = tabs.map(tab => {
return (
<li>
{editTabNameMode && currentTab.id === tab.id ? (
<input
value={tab.name}
onBlur={this.handleOnBlur}
onChange={this.handleEditTabName}
/>
) : (
<button
className={currentTab.id === tab.id ? "tab active" : "tab"}
onClick={() => this.handleSelectTab(tab)}
onDoubleClick={() => this.handleDoubleClick(tab)}
>
{tab.name}
</button>
)}
</li>
);
});
return <ul className="nav nav-tabs">{allTabs}</ul>;
};
handleSelectTab = tab => {
this.setState({
currentTab: tab,
editMode: false,
editTabNameMode: false
});
};
handleAddTab = () => {
const { tabs } = this.state;
const newTabObject = {
id: uuid(),
name: `Tab ${tabs.length + 1}`,
content: `This is Tab ${tabs.length + 1}`,
DroppedDetails: []
};
this.setState({
tabs: [...tabs, newTabObject],
currentTab: newTabObject,
editMode: false,
editTabNameMode: false
});
};
handleDeleteTab = tabToDelete => {
const { tabs } = this.state;
const tabToDeleteIndex = tabs.findIndex(tab => tab.id === tabToDelete.id);
const updatedTabs = tabs.filter((tab, index) => {
return index !== tabToDeleteIndex;
});
const previousTab =
tabs[tabToDeleteIndex - 1] || tabs[tabToDeleteIndex + 1] || {};
this.setState({
tabs: updatedTabs,
editMode: false,
editTabNameMode: false,
currentTab: previousTab
});
};
setEditMode = () => {
this.setState({
editMode: !this.state.editMode
});
};
handleContentChange = e => {
const { tabs, currentTab } = this.state;
const updatedTabs = tabs.map(tab => {
if (tab.name === currentTab.name) {
return {
...tab,
content: e.target.value
};
} else {
return tab;
}
});
this.setState({
tabs: updatedTabs,
currentTab: {
...currentTab,
content: e.target.value
}
});
};
handleOnDetailBlur = (id, index) => {
const { tabs, currentTab } = this.state;
const updatedCurrentTab = { ...currentTab };
updatedCurrentTab.DroppedDetails = updatedCurrentTab.DroppedDetails.map(
(detail, detailIndex) => {
if (index == detailIndex) {
return {
...detail,
editing: false
};
} else {
return detail;
}
}
);
const updatedTabs = tabs.map(tab => {
if (tab.id == id) {
return {
...tab,
DroppedDetails: tab.DroppedDetails.map((detail, detailIndex) => {
if (detailIndex == index) {
return {
...detail,
editing: false
};
} else {
return detail;
}
})
};
} else {
return tab;
}
});
this.setState({
tabs: updatedTabs || [],
currentTab: updatedCurrentTab
});
};
handleDoubleClickDetail = (id, index) => {
const { tabs, currentTab } = this.state;
const updatedCurrentTab = { ...currentTab };
updatedCurrentTab.DroppedDetails = updatedCurrentTab.DroppedDetails.map(
(detail, detailIndex) => {
if (index == detailIndex) {
return {
...detail,
editing: true
};
} else {
return detail;
}
}
);
const updatedTabs = tabs.map(tab => {
if (tab.id == id) {
return {
...tab,
DroppedDetails: tab.DroppedDetails.map((detail, detailIndex) => {
if (detailIndex == index) {
return {
...detail,
editing: true
};
} else {
return detail;
}
})
};
} else {
return tab;
}
});
this.setState({
tabs: updatedTabs || [],
currentTab: updatedCurrentTab
});
};
createContent = () => {
const { currentTab } = this.state;
return (
<div>
<div>
<p>{currentTab.content}</p>
<div>
<h4>Dropped Details</h4>
{currentTab.DroppedDetails ? (
<div>
{currentTab.DroppedDetails.map((detail, index) => {
if (detail.editing) {
return (
<div>
<input
value={detail.name}
onChange={e =>
this.handleDetailChange(e, currentTab.id, index)
}
onBlur={() =>
this.handleOnDetailBlur(currentTab.id, index)
}
/>
</div>
);
} else {
return (
<p
onDoubleClick={() =>
this.handleDoubleClickDetail(currentTab.id, index)
}
>
{detail.name}
</p>
);
}
})}
</div>
) : (
""
)}
</div>
</div>
{currentTab.id ? (
<div style={{ display: "flex", justifyContent: "space-between" }}>
<button className="edit-mode-button" onClick={this.setEditMode}>
Edit
</button>
<button onClick={() => this.handleDeleteTab(currentTab)}>
Delete
</button>
</div>
) : (
""
)}
</div>
);
};
render() {
const { currentTab, editMode } = this.state;
return (
<div className="container">
<div className="well">
<button className="add-tab-button" onClick={this.handleAddTab}>
<i className="text-primary fas fa-plus-square" /> Add Tab
</button>
{this.createTabs()}
<div className="tab-content">
{editMode ? (
<div>
<textarea
onChange={this.handleContentChange}
value={currentTab.content}
/>
<button className="save-button" onClick={this.setEditMode}>
Done
</button>
</div>
) : (
this.createContent()
)}
</div>
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
To activate "edit-mode" double-click a dropped-detail. The input should appear in its place and you can type in the new text. When complete, click out of the input and it will finalize the updated text :)
Related
What I want is an example about how to make the drag and drop of my Table that works properly, but I cannot figure out how to make it works in (functional components)
My code :
function Preview() {
const { name } = useParams();
const [fieldsOfForm, setFieldsOfForm] = useState([]);
const [selectedForm, setSelectedForm] = useState([]);
const columns = [
{
title: 'Posição',
dataIndex: 'pos',
width: 30,
className: 'drag-visible',
render: () =>
<MenuOutlined style={{ cursor: 'grab', color: '#999' }}/>
},
{
title: "Form Name",
dataIndex: "field",
key: "field",
render: (text) => <a>{text}</a>,
},
{
title: "Tipo",
dataIndex: "fieldtype",
key: "fieldtype",
},
];
useEffect(() => {
let mounted = true;
let loadingStates = loading;
if (mounted) {
setFieldsOfForm(location.state);
loadingStates.allFields = false;
setLoading(false);
}
return () => (mounted = false);
}, [selectedForm]);
return (
//Some jsx....
<Row gutter={1}>
<Col span={1}></Col>
<Table dataSource={fieldsOfForm}
columns= {columns}/>
</Row>
// More jsx...
);
}
export default Preview;
Everything that I found on internet about this drag and drop from the lib of antd is in class component , but I wanted to make it works in my functional one.
Example that I found :
multi row drag-able table (antd)
I want some example in function component if someone has tried it already and could help me ?
Here's a functional working example:
import React from "react";
import "antd/dist/antd.css";
import { Table } from "antd";
import {
sortableContainer,
sortableElement,
sortableHandle
} from "react-sortable-hoc";
import { MenuOutlined } from "#ant-design/icons";
const data = [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
index: 0
},
{
key: "2",
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park",
index: 1
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park",
index: 2
},
{
key: "4",
name: "4",
age: 32,
address: "New York No. 1 Lake Park",
index: 3
},
{
key: "5",
name: "5",
age: 42,
address: "London No. 1 Lake Park",
index: 4
},
{
key: "6",
name: "6",
age: 32,
address: "Sidney No. 1 Lake Park",
index: 5
}
];
const DragHandle = sortableHandle(({ active }) => (
<MenuOutlined style={{ cursor: "grab", color: active ? "blue" : "#999" }} />
));
const SortableItem = sortableElement((props) => <tr {...props} />);
const SortableContainer = sortableContainer((props) => <tbody {...props} />);
function SortableTable() {
const [dataSource, setDataSource] = React.useState(data);
const [selectedItems, setSelectedItems] = React.useState([]);
const getColumns = () => {
return [
{
title: "Sort",
dataIndex: "",
width: 30,
className: "drag-visible",
render: (d, dd, i) => (
<>
<DragHandle active={selectedItems.includes(i)} />
</>
)
},
{
title: "Name",
dataIndex: "name",
className: "drag-visible"
},
{
title: "Age",
dataIndex: "age"
},
{
title: "Address",
dataIndex: "address"
}
];
};
const merge = (a, b, i = 0) => {
let aa = [...a];
return [...a.slice(0, i), ...b, ...aa.slice(i, aa.length)];
};
const onSortEnd = ({ oldIndex, newIndex }) => {
let tempDataSource = dataSource;
if (oldIndex !== newIndex) {
if (!selectedItems.length) {
let movingItem = tempDataSource[oldIndex];
tempDataSource.splice(oldIndex, 1);
tempDataSource = merge(tempDataSource, [movingItem], newIndex);
} else {
let filteredItems = [];
selectedItems.forEach((d) => {
filteredItems.push(tempDataSource[d]);
});
let newData = [];
tempDataSource.forEach((d, i) => {
if (!selectedItems.includes(i)) {
newData.push(d);
}
});
tempDataSource = [...newData];
tempDataSource = merge(tempDataSource, filteredItems, newIndex);
}
setDataSource(tempDataSource);
setSelectedItems([]);
}
};
const DraggableContainer = (props) => (
<SortableContainer
useDragHandle
disableAutoscroll
helperClass="row-dragging"
onSortEnd={onSortEnd}
{...props}
/>
);
const DraggableBodyRow = ({ className, style, ...restProps }) => {
// function findIndex base on Table rowKey props and should always be a right array index
const index = dataSource.findIndex(
(x) => x.index === restProps["data-row-key"]
);
return (
<SortableItem
index={index}
{...restProps}
selected={selectedItems.length}
onClick={(e) => {
if (e.ctrlKey || e.metaKey) {
selectedItems.includes(index)
? selectedItems.splice(selectedItems.indexOf(index), 1)
: selectedItems.push(index);
setSelectedItems(selectedItems);
} else {
setSelectedItems([]);
}
}}
/>
);
};
return (
<>
<h3>"CNTRL + Click" to select multiple items</h3>
<Table
pagination={false}
dataSource={dataSource}
columns={getColumns()}
rowKey="index"
components={{
body: {
wrapper: DraggableContainer,
row: DraggableBodyRow
}
}}
/>
{selectedItems.length ? <>{selectedItems.length} items selected </> : ""}
</>
);
}
You can play with it in Sandbox
I have many div in cascade.
and I want to apply alternated colors to my golbal div ( green, yellow for example ). But i want that colors start from the begening of the global div not from the begening of the div that contains it..
I created this using recursivity.
This is what i have ( I displayed div borders to explain the design of the page i have. )
This is what i want
React code
<div>
{
intervenants.map(i => {
return (updateListDiv(i))
})
}
</div>
const updateListDiv = (intervenant) => {
if (intervenant.children && intervenant.children.length > 0) {
return (
intervenant.children.map((int, index) => {
let n = int.name + ' ( ' + int.profile.name + ' ) '
return (<div className="a b" key={Math.random()}>
<a> {int.name} </a>
( {int.profile.name} )
{updateListDiv(int)}
</div>)
})
)
} else {
}
}
css
.a {
margin-left: 30px;
}
.b {
line-height: 24pt;
border: solid 1px black;
}
Every item needs to know the previous color. So you can store the state globally, or (probably better) pass the state to your updateListDiv function.
Here I call the state isEven:
import React from 'react';
const intervenants = [
{ name: 'a', profile: { name: 'pa' }, children: [ { name: 'a.a', profile: { name: 'pa.a' }, children: null }, { name: 'a.b', profile: { name: 'pa.b' }, children: null }, ] },
{ name: 'b', profile: { name: 'pb' }, children: [ { name: 'b.a', profile: { name: 'pb.a' }, children: null }, { name: 'b.b', profile: { name: 'pb.b' }, children: null }, ] },
{ name: 'c', profile: { name: 'pc' }, children: [ { name: 'c.a', profile: { name: 'pc.a' }, children: null }, { name: 'c.b', profile: { name: 'pc.b' }, children: null }, ] },
];
const updateListDiv = (intervenant, isEven) => {
if (!intervenant.children || intervenant.children.length < 1) {
return;
}
return (
intervenant.children.map((int, index) => {
isEven = !isEven;
return (<div
key={ index }
style={{
marginLeft: '30px',
border: 'solid 1px',
backgroundColor: isEven ? '#ff0' : '#0f0',
}}
>
{ int.name }
{ updateListDiv(int, isEven) }
</div>);
})
);
}
export const ColorTree = ()=>{
return (<div>
{ updateListDiv({ children: intervenants }, false) }
</div>);
};
Alternatively, you can pass down an overall index, and check for modulo-2.
I like this approach more, because this is more flexible (it might come in handy to have a "total" index inside the childs, and it also becomes possible to e.g. use modulo-3 or something)
const updateListDiv = (intervenant, overAllIndex) => {
// ...
return (
intervenant.children.map((int, index) => {
overAllIndex++;
return (<div
style={{
backgroundColor: (overAllIndex % 2) ? '#ff0' : '#0f0',
}}
>
{ updateListDiv(int, overAllIndex) }
</div>);
})
);
}
I'm trying to perform CRUD operations in React using react-table-6 library, since its lightweight and has basic functionalities. I dont want my bundle size to be more.
Here is my Code https://codesandbox.io/s/crud-using-react-table-6-rs2r9
Whenever I try to edit and change values using input, the table re-renders. I think the issue is because, the Cell inside the columns re-renders when I change the state. But I'm not able to get a workaround for this. Any help would be appreciated greatly. Thanks.
I did some experiments and found that, react-table-6 library doesn't support input element.
However with contentEditable div, It seems to work perfectly.
Code with Input
import React from "react";
import "./styles.css";
import ReactTable from "react-table-6";
import "react-table-6/react-table.css";
export default function App() {
const [state, setState] = React.useState({
data: [
{
name: "Adam",
age: 20
},
{
name: "Eve",
age: 19
},
{
name: "Abel",
age: 4
},
{
name: "Coin",
age: 5
}
],
edit: {}
});
return (
<ReactTable
data={state.data}
columns={[
{
Header: "Name",
accessor: "name",
Cell: ({ value, ...props }) =>
state.edit.name === props.original.name ? (
<input
value={state.edit.name}
onChange={(e) => {
console.log(e.target.value);
setState({
...state,
edit: { ...state.edit, name: e.target.value }
});
}}
/>
) : (
value
)
},
{
Header: "Age",
accessor: "age"
},
{
Cell: ({ value, ...props }) =>
Object.keys(state.edit).length > 0 &&
state.edit.name === props.original.name ? (
<button>Save</button>
) : (
<button
onClick={(e) => setState({ ...state, edit: props.original })}
>
Edit
</button>
)
}
]}
/>
);
}
Code with content editable div
import React from "react";
import "./styles.css";
import ReactTable from "react-table-6";
import "react-table-6/react-table.css";
export default function App() {
const [state, setState] = React.useState({
data: [
{ id: 1, name: "Adam", age: 20 },
{ id: 2, name: "Eve", age: 19 },
{ id: 3, name: "Abel", age: 4 },
{ id: 4, name: "Coin", age: 5 }
],
edit: {}
});
return (
<ReactTable
data={state.data}
columns={[
{
Header: "Name",
accessor: "name",
Cell: ({ value, ...props }) => (
<div
style={
state.edit.id === props.original.id
? {
backgroundColor: "#ddd",
padding: "5px",
outline: "1px solid #0000ff"
}
: {}
}
contentEditable={state.edit.id === props.original.id}
suppressContentEditableWarning
onBlur={(e) => {
setState({
...state,
edit: { ...state.edit, name: e.target.innerHTML }
});
}}
dangerouslySetInnerHTML={{
__html:
state.edit.id === props.original.id ? state.edit.name : value
}}
/>
)
},
{
Header: "Age",
accessor: "age"
},
{
Cell: ({ value, ...props }) =>
Object.keys(state.edit).length > 0 &&
state.edit.id === props.original.id ? (
<button
onClick={(e) => {
let newdata = state.data;
newdata.map((d) => {
if (d.id === state.edit.id) {
d.name = state.edit.name;
}
return d;
});
console.log(newdata);
setState({ ...state, data: newdata, edit: {} });
}}
>
Save
</button>
) : (
<button
onClick={(e) => setState({ ...state, edit: props.original })}
>
Edit
</button>
)
}
]}
defaultPageSize={5}
/>
);
}
I am basically a beginner in React. I have a dashboard page where I display a React Table. I have a customize button which will open a popup page, this popup page has some 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.
This is my parent component - the parent page is the page with the ReactTable, there are 10 columns now, not 8 as shown in the image.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import ReactTable from 'react-table';
import LoadingComponent from '../shared/loading/LoadingComponent';
import InputComponent from '../shared/input/InputComponent';
import { makeData } from '../../util/Utils';
import CustomizedView from './customized_view/CustomizedView';
import filter from '../../assets/svg/filter.svg';
import config from '../../../framework/config';
//import EnquiryDetails from './enquiry_details/enquiryDetails';
const searchIcon = config.assetUrl + '/table_filter/svg/search-icon.svg';
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
filterState: {},
data: makeData(),
searchText: '',
isFilterOpen: false,
isCustomizedOpen: false,
isFiltered: false,
isSearched: false,
searchedTableData: [],
filteredTableData: [],
filterItems: [
{ id: 1, value: 'Col 1', isChecked: true },
{ id: 2, value: 'Col 2', isChecked: true },
{ id: 3, value: 'Col 3', isChecked: true },
{ id: 4, value: 'Col 4', isChecked: true },
{ id: 5, value: 'Col 5', isChecked: true },
{ id: 6, value: 'Col 6', isChecked: true },
{ id: 7, value: 'Col 7', isChecked: true },
{ id: 8, value: 'Col 8', isChecked: true },
{ id: 9, value: 'Col 9', isChecked: true },
{ id: 10, value: 'Col 10', isChecked: true },
],
};
this.handleFilterClickinv = this.handleFilterClickinv.bind(this);
this.handleCustClickinv = this.handleCustClickinv.bind(this);
}
getTopBar() {
return (
<div className='top-bar-div'>
<div className='heading-div'>Dashboard</div>
</div>);
}
getFilterBar() {
const searchIconImg = (<img src={searchIcon} alt="" />);
if(this.state.isFiltered) table = this.state.filteredTableData;
if(this.state.isSearched) table = this.state.searchedTableData;
return (
<div className='table-header-div' >
<div className="filter-container-div">
</div>
<div className='search-enquiry-div'>
<div className="search-container">
<div className="search-input-container-div">
<InputComponent
height="41px"
width="280px"
inputClassName="order-filter-input"
placeholder='Search'
type="text"
value={this.state.searchText}
icon={searchIconImg}
onChange={this.onSearch}
/>
</div>
</div>
</div>
<div className="filter-icon-div-main">
<div className="custom-icon-div" onClick={() => { this.handleCustClickinv(); }}>
<div className='customize-view-inventory'>Customized View </div>
</div>
</div>
</div>);
}
getColumns() {
const columns = [
{
id: "col16",
Header: () => {
return (
<div>
<div className="col1-heading">Col 1</div>
<div className="col6-heading">Col 6</div>
</div>
);
},
accessor: d => {
return (
<div>
<div className="col1">{d.firstName}</div>
<div className="col6">{d.lastName}</div>
</div>
);
},
width: 200
},
{
id: "col27",
Header: () => {
return (
<div>
<div className="col2-heading">Col 2</div>
<div className="col7-heading">Col 7</div>
</div>
);
},
accessor: d => {
return (
<div>
<div className="col2">{d.firstName}</div>
<div className="col7">{d.lastName}</div>
</div>
);
},
width: 200
},
{
id: "col38",
Header: () => {
return (
<div>
<div className="col3-heading">Col 3</div>
<div className="col8-heading">Col 8</div>
</div>
);
},
accessor: d => {
return (
<div>
<div className="col3">{d.firstName}</div>
<div className="col8">{d.lastName}</div>
</div>
);
},
width: 200
},
{
id: "col49",
Header: () => {
return (
<div>
<div className="col4-heading">Col 4</div>
<div className="col9-heading">Col 9</div>
</div>
);
},
accessor: d => {
return (
<div>
<div className="col4">{d.firstName}</div>
<div className="col9">{d.lastName}</div>
</div>
);
},
width: 200
},
{
id: "col510",
Header: () => {
return (
<div>
<div className="col5-heading">Col 5</div>
<div className="col10-heading">Col 10</div>
</div>
);
},
accessor: d => {
return (
<div>
<div className="col5">{d.firstName}</div>
<div className="col10">{d.lastName}</div>
</div>
);
},
width: 200
},
{
id: "col11",
Header: "Col 11",
columns: [
{
id: "scol11a",
Header: "Sub Col 11a",
accessor: d => {
return d.firstName;
},
width: 80
},
{
id: "scol11b",
Header: "Sub Col 11b",
accessor: d => {
return d.firstName;
},
width: 80
},
{
id: "scol11c",
Header: "Sub Col 11c",
accessor: d => {
return d.firstName;
},
width: 80
},
{
id: "scol11d",
Header: "Sub Col 11d",
accessor: d => {
return d.firstName;
},
width: 80
}
]
},
{
id: "col12",
Header: "Col 12",
columns: [
{
id: "scol12a",
Header: "Sub Col 12",
accessor: d => {
return d.firstName;
},
width: 80
}
]
},
{
id: "col13",
Header: "Col 13",
columns: [
{
id: "scol13a",
Header: "Sub Col 13a",
accessor: d => {
return d.firstName;
},
width: 80
},
{
id: "scol13b",
Header: "Sub Col 13b",
accessor: d => {
return d.firstName;
},
width: 80
},
{
id: "scol13c",
Header: "Sub Col 13c",
accessor: d => {
return d.firstName;
},
width: 80
},
{
id: "scol13d",
Header: "Sub Col 13d",
accessor: d => {
return d.firstName;
},
width: 80
}
]
}
];
// if(this.state.isDisabled) {
// columns.splice(0, 1);
// columns.splice(columns.length - 1, 1);
// }
return columns;
}
/* function (item)*/
getCheckBox(item) {
return (
<div>
<input
value={item.id}
type='checkbox'
checked={item.isChecked}
onClick={(e) => { this.handleCheckChildElement(e); }}
/>
{item.value}
</div>);
}
handleCheckChildElement(event) {
const { items } = this.state.filterItems;
for (let i = 0; i < items.length; i = i + 1) {
if(items[i].id === +event.target.value) {
items[i].isChecked = !items[i].isChecked;
break;
}
}
this.setState({ filterItems: items });
console.log('handleCheckChildElement : > Items : ' + JSON.stringify(filterItems));
}
handleFilterClickinv() {
if(this.state.isCustomizedOpen) {
this.setState({ isCustomizedOpen: false });
}
const currentState = this.state.isFilterOpen;
this.setState({ isFilterOpen: !currentState });
}
handleCustClickinv() {
if(this.state.isFilterOpen) {
this.setState({ isFilterOpen: false });
}
const currentState = this.state.isCustomizedOpen;
this.setState({ isCustomizedOpen: !currentState });
}
resetFilter() {
const { items } = this.state.filterItems;
console.log("In resetFilter this.state.filterItems : " + JSON.stringify(this.state.filterItems));
console.log("In resetFilter : " + items);
for (let i = 0; i < items.length; i = i + 1) {
items[i].isChecked = true;
}
this.setState({ filterItems: items });
console.log('Reset filter : > Items : ' + JSON.stringify(filterItems));
}
render() {
const { data } = this.state;
return (
<div className='dashboard-body-container'>
<div className='hide'> {this.state.activeStepId}</div>
{this.getTopBar()}
{this.state.isCustomizedOpen &&
<CustomizedView
// items={filterItems}
getCheckBox={(item) => { this.getCheckBox(item); }}
// handleCheckChildElement={(event) => { this.handleCheckChildElement(event); }}
resetFilter={this.resetFilter()}
/>}
<div className='whiteBackground'>
{this.getFilterBar()}
<div>
{this.state.isLoading &&
<LoadingComponent />
}
</div>
<div>
<ReactTable
data={data}
columns={this.getColumns()}
showPagination
defaultPageSize={10}
/>
</div>
</div>
</div>
);
}
}
Dashboard.propTypes = {
};
function mapStateToProps(state) {
return {
auth: state.auth
};
}
export default connect(mapStateToProps)(Dashboard);
This is my child component - in my child page (the page in which checkboxes are displayed) I have 4 major functions - getCheckbox - to create checkbox, handlecheckchild - handle the check uncheck events, resetFilter and applyFilter. I have not still finished the code of applyFilter.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { ActionCreators } from '../../../actions';
import ButtonComponent from '../../common/button/ButtonComponent';
class CustomizedView extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
const { items } = this.state;
return (
<div className='DashboardFilter-container-custom' >
<div className='bottomBar'>
<ButtonComponent
text='Apply'
className='activeButton filterMargin-div'
width='100'
display='inline-block'
// onClick={() => { this.props.applyFilter(); }}
/>
<ButtonComponent
text='Reset View'
className='greyedButton clear-div-filter'
width='100'
display='block'
marginTop='60'
onClick={() => { this.props.resetFilter(); }}
/>
</div>
<div>
<div className='column-points-text'>
<span> Columns </span>
</div>
<div className="App">
{items.map((item) => {
return this.props.getCheckBox(item);
})}
</div>
</div>
</div>
);
}
}
CustomizedView.propTypes = {
// items: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
getCheckBox: PropTypes.func.isRequired,
// handleCheckChildElement: PropTypes.func.isRequired,
resetFilter: 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);
I have written all the major functions (for operations in child page in my parent and passing those functions as props - getCheckBox and resetFilter from parent page to child page.
I am getting error in the this line of my parent page - Cannot read property 'length' of undefined
for (let i = 0; i < items.length; i = i + 1) {
I am basically a beginner in React. So please help to me understand what is causing the error.
Also tell me whether it is okay to write all child functions in parent page?
Edit 1 - According to Code Maniac's answer
I changed my code in parent to
handleCheckChildElement(event) {
const { filterItems } = this.state;
for (let i = 0; i < filterItems.length; i = i + 1) {
if(filterItems[i].id === +event.target.value) {
filterItems[i].isChecked = !filterItems[i].isChecked;
break;
}
}
this.setState({ filterItems });
console.log('handleCheckChildElement : > Items : ' + JSON.stringify(filterItems));
}
resetFilter() {
const { filterItems } = this.state;
for (let i = 0; i < filterItems.length; i = i + 1) {
filterItems[i].isChecked = true;
}
this.setState({ filterItems });
console.log('Reset filter : > Items : ' + JSON.stringify(filterItems));
}
I am now getting error - The prop resetFilter is marked as required in CustomizedView, but its value is undefined.
I am getting error and warning in an infinite loop.
Edit 2 - if change my code like this
handleCheckChildElement(event) {
const { items } = this.state;
for (let i = 0; i < items.length; i = i + 1) {
if(items[i].id === +event.target.value) {
items[i].isChecked = !items[i].isChecked;
break;
}
}
this.setState({ filterItems: items });
console.log('handleCheckChildElement : > Items : ' + JSON.stringify(filterItems));
}
resetFilter() {
const { items } = this.state;
for (let i = 0; i < items.length; i = i + 1) {
items[i].isChecked = true;
}
this.setState({ filterItems: items });
console.log('Reset filter : > Items : ' + JSON.stringify(filterItems));
}
I am geeting yarn build error - Unused state field: 'filterItems' react/no-unused-state
Well problem in you code is here
const { items } = this.state.filterItems;
You're trying to find a key ( item property ) in array ( fillterItems is an array ) which is why you're getting undefined .
This will be same as below example.
let a = [{v:1},{2:2}];
let {items} = a;
console.log(items)
And what you should do instead to get proper output.
const { filterItems } = this.state;
Demo code
let a = {
items : [{v:1},{2:2}],
itemstemp : "temp"
}
let {items} = a;
console.log(items)
I had a very long form which around 20 different fields and I displayed those input area using map function. I want to valid the input data when I click the submit button and jump to the corresponding input required box.
const ReportFields = [
{
title: "Report Title*",
field: "report_title",
type: "text",
required: true
},
{
title: "Submitting Agency*",
field: "submitting_agency",
type: "text",
required: true
},
{
title: "Division*",
field: "division",
type: "select",
required: true
},
{
title: "Committee*",
field: "committee",
type: "select",
required: true
},
{
title: "Assigned Contact*",
field: "assigned_contact",
type: "select",
required: true
},
{
title: "Other Recipients",
field: "other_recipients",
type: "text",
required: false
}];
class App extends Component {
state = {
report: {
report_title: "",
submitting_agency: "",
division: "",
committee: "",
assigned_contact: "",
other_recipients: ""
},
errorMessage: "",
refs: {}
}
componentDidMount() {
this.registerRefs();
}
registerRefs = () => {
const refs = ReportFields.reduce((acc, current) => {
const ref = React.createRef();
acc[current.field] = ref;
return acc;
}, {});
this.setState({ refs });
}
onSubmit = (e) => {
e.preventDefault();
for (let i = 0; i < ReportFields.length; i++) {
const curt = ReportFields[i];
if (curt.required && this.state.report[curt.field] === "") {
this.setState({errorMessage: `${curt.title} cannot be empty!`});
this.state.refs[curt.field].current.focus();
break;
}
}
}
render() {
const display = ReportFields.map((field, idx) => {
return (
<div key={idx}>
<p>{field.title}</p>
<input
type={field.type}
onChange={(e) => {
this.setState({
report: {...this.state.report, [field.field]: e.target.value}
})
}}
ref={this.state.refs[field.field]}
></input>
</div>
);
})
return (
<div className="App">
{display}
<input type="button" value="submit" onClick={this.onSubmit}/>
</div>
);
}
}
export default App;
I tried to use react refs but it doesn't work, any idea?
Also, I am actually using these content in react modal, will this be one of the reason why it doesn't work?
Ok here is a solution I know who can work but I don't say it's the best one. A working example here https://codesandbox.io/s/94v4r6w7kr. As you can see when you click submit you jump to password input.
How do that work ? First as you can see we need a way to save all the ref we gonna create. I save it in the state refs here. The way that work is a loop over each field and for each one I createRef and add this to an object. I use this object inside the state. When you want to use it after that, you then can call this.state.refs[thenameoftheinput].current.focus().
This is an example, and I let you make it work with your own data. But I hope that can give you an idea :)
const ReportFields = [
{
title: "Report Title*",
field: "report_title",
type: "text",
required: true
},
{
title: "Submitting Agency*",
field: "submitting_agency",
type: "text",
required: true
},
{
title: "Division*",
field: "division",
type: "select",
required: true
},
{
title: "Committee*",
field: "committee",
type: "select",
required: true
},
{
title: "Assigned Contact*",
field: "assigned_contact",
type: "select",
required: true
},
{
title: "Other Recipients",
field: "other_recipients",
type: "text",
required: false
}
];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
refs: {}
};
}
componentDidMount() {
this.registerRefs();
}
registerRefs = () => {
const refs = ReportFields.reduce((acc, current) => {
const ref = React.createRef();
acc[current.field] = ref;
return acc;
}, {});
this.setState({ refs });
};
focusTextInput = () => {
this.state.refs.division.current.focus();
};
render() {
const inputs = ReportFields.map(el => {
return <input placeholder={el.title} ref={this.state.refs[el.field]} />;
});
return (
<div>
<form>
{inputs}
<input type="button" value="submit" onClick={this.focusTextInput} />
</form>
</div>
);
}
}
Iterate through all fields and create a separate ref for each one of them. Use a unique identifier (as a suggestion - the name property) to access the ref later.
class App extends Component {
constructor(props) {
super(props);
this.focusTextInput = this.focusTextInput.bind(this);
// Fields
this.ReportFields = [
{
type: "text",
name: "firstname",
title: "First Name"
},
{
type: "text",
name: "lastname",
title: "Last Name"
}
];
this.inputRefs = this.ReportFields.reduce((acc, field) => ({
...acc,
[field.name]: React.createRef()
}), {});
}
state = {
a: {
b: "",
c: "",
d: "",
e: "",
f: "",
g: "",
h: "",
i: "",
j: "",
k: "",
l: "",
m: "",
n: "",
o: "",
p: "",
q: "",
r: "",
s: "",
t: "",
u: "",
v: "",
w: "",
x: "",
y: ""
},
errorMessage: ""
};
focusTextInput() {
// Focus on the input you wish, in this case "firstname"
console.log(this.inputRefs["firstname"].current.focus());
}
render() {
const display = this.ReportFields.map((field, idx) => {
return (
<div key={idx}>
<p>{field.title}</p>
<input
type={field.type}
onChange={e => {
this.setState({
report: { ...this.state.report, [field.field]: e.target.value }
});
}}
ref={this.inputRefs[field.name]}
/>
</div>
);
});
return (
<div className="App">
{display}
<input type="button" value="submit" onClick={this.focusTextInput} />
</div>
);
}
}