Pass dynamic content to the react table sub component - javascript

I am using react table v6 for grid purposes. I am trying to implement a subcomponent where in the data to this sub component needs to be passed dynamically. This sub component should expand and collapse on click of arrows. I have tried the following , but the sub component is not rendering any data. I am creating a wrapper for this, the data to the subcomponent should be passed dynamically based on the source data.
https://codesandbox.io/s/react-table-row-table-subcompoentn-sk14i?file=/src/DataGrid.js
import * as React from "react";
import ReactTable from "react-table";
import "react-table/react-table.css";
export default class DataGrid extends React.Component {
renderSubComponent = original => {
console.log(original);
return (
original.nested &&
original.nested.map((i, key) => (
<React.Fragment key={key}>
<div>{i.name}</div>
<div>{i.value}</div>
</React.Fragment>
))
);
};
render() {
return (
<ReactTable
data={this.props.data}
columns={this.props.columns}
SubComponent={this.renderSubComponent}
/>
);
}
}
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
columns: [],
allData: []
};
}
componentDidMount() {
this.getData();
this.getColumns();
}
getData = () => {
const data = [
{
firstName: "Jack",
status: "Submitted",
nested: [
{
name: "test1",
value: "NA"
},
{
name: "test2",
value: "NA"
}
],
age: "14"
},
{
firstName: "Simon",
status: "Pending",
nested: [
{
name: "test3",
value: "NA"
},
{
name: "test4",
value: "Go"
}
],
age: "15"
}
];
this.setState({ data });
};
getColumns = () => {
const columns = [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Status",
accessor: "status"
},
{
Header: "Age",
accessor: "age"
}
];
this.setState({ columns });
};
onClickRow = rowInfo => {
this.setState({ allData: rowInfo }, () => {
console.log(this.state.allData);
});
};
render() {
return (
<>
<DataGrid
data={this.state.data}
columns={this.state.columns}
rowClicked={this.onClickRow}
/>
</>
);
}
}
render(<App />, document.getElementById("root"));

try spread the argument and it will work :
...
renderSubComponent = ({original}) => {
...

Related

REACTJ - convert class component to functionnal component

i'm new to react and i'm trying to convert this class based component to a functionnal component but i get an error of state, how can i convert it please ?
This is my components :)
sandbox link
Thank you
In this example, it is quite straight forward as there are no component life cycle methods. You can just define all the methods in the function, and return the component.
function Demo() {
const [state, setState] = React.useState({
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [],
allCheckedKeys: [],
selectedKeys: [],
newTreeView: false,
newTreeData: []
});
const onExpand = (expandedKeys) => {
console.log("onExpand", expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setState({
...state,
expandedKeys,
autoExpandParent: false
});
};
const onCheck = (checkedKeys, e) => {
const allCheckedKeys = [...checkedKeys, ...e.halfCheckedKeys];
console.log("onCheck", allCheckedKeys);
console.log(createNewTreeData(treeData, allCheckedKeys));
setState((prevState) => ({
...prevState,
allCheckedKeys,
checkedKeys
}));
};
const onSelect = (selectedKeys, info) => {
console.log("onSelect", info);
setState({ ...state, selectedKeys });
};
const renderTreeNodes = (data) =>
data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} />;
});
const createTree = () => {
setState((prevState) => ({
...prevState,
newTreeView: true,
newTreeData: createNewTreeData(treeData, prevState.allCheckedKeys)
}));
};
return (
<>
<Tree
checkable
onExpand={onExpand}
expandedKeys={state.expandedKeys}
autoExpandParent={state.autoExpandParent}
onCheck={onCheck}
checkedKeys={state.checkedKeys}
onSelect={onSelect}
selectedKeys={state.selectedKeys}
>
{renderTreeNodes(treeData)}
</Tree>
<button onClick={createTree}>Validate</button>
{state.newTreeView && <Tree>{renderTreeNodes(state.newTreeData)}</Tree>}
</>
);
}
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Tree } from 'antd';
const treeData = [
{
title: '0-0',
key: '0-0',
children: [
{
title: '0-0-0',
key: '0-0-0',
children: [
{
title: '0-0-0-0',
key: '0-0-0-0',
},
{
title: '0-0-0-1',
key: '0-0-0-1',
},
{
title: '0-0-0-2',
key: '0-0-0-2',
},
],
},
{
title: '0-0-1',
key: '0-0-1',
children: [
{
title: '0-0-1-0',
key: '0-0-1-0',
},
{
title: '0-0-1-1',
key: '0-0-1-1',
},
{
title: '0-0-1-2',
key: '0-0-1-2',
},
],
},
{
title: '0-0-2',
key: '0-0-2',
},
],
},
{
title: '0-1',
key: '0-1',
children: [
{
title: '0-1-0-0',
key: '0-1-0-0',
},
{
title: '0-1-0-1',
key: '0-1-0-1',
},
{
title: '0-1-0-2',
key: '0-1-0-2',
},
],
},
{
title: '0-2',
key: '0-2',
},
];
const Demo = () => {
const [expandedKeys, setExpandedKeys] = useState(['0-0-0', '0-0-1']);
const [checkedKeys, setCheckedKeys] = useState(['0-0-0']);
const [selectedKeys, setSelectedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const onExpand = (expandedKeysValue) => {
console.log('onExpand', expandedKeysValue); // if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setExpandedKeys(expandedKeysValue);
setAutoExpandParent(false);
};
const onCheck = (checkedKeysValue) => {
console.log('onCheck', checkedKeysValue);
setCheckedKeys(checkedKeysValue);
};
const onSelect = (selectedKeysValue, info) => {
console.log('onSelect', info);
setSelectedKeys(selectedKeysValue);
};
return (
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={treeData}
/>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
I have updated the code using ES6 arrow functions resulting in shorter and simpler code than traditional functional components.
import React,{useState} from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Tree } from "antd";
const { TreeNode } = Tree;
const treeData = [
{
title: "0-0",
key: "0-0",
children: [
{
title: "0-0-0",
key: "0-0-0",
children: [
{ title: "0-0-0-0", key: "0-0-0-0" },
{ title: "0-0-0-1", key: "0-0-0-1" },
{ title: "0-0-0-2", key: "0-0-0-2" }
]
},
{
title: "0-0-1",
key: "0-0-1",
children: [
{ title: "0-0-1-0", key: "0-0-1-0" },
{ title: "0-0-1-1", key: "0-0-1-1" },
{ title: "0-0-1-2", key: "0-0-1-2" }
]
},
{
title: "0-0-2",
key: "0-0-2"
}
]
},
{
title: "0-1",
key: "0-1",
children: [
{ title: "0-1-0-0", key: "0-1-0-0" },
{ title: "0-1-0-1", key: "0-1-0-1" },
{ title: "0-1-0-2", key: "0-1-0-2" }
]
},
{
title: "0-2",
key: "0-2"
}
];
const createNewTreeData = (treeData, checkedKeys) => {
return treeData.reduce((acc, treeDataItem) => {
if (checkedKeys.includes(treeDataItem.key)) {
if (treeDataItem.children) {
acc.push({
...treeDataItem,
children: createNewTreeData(treeDataItem.children, checkedKeys)
});
} else {
acc.push(treeDataItem);
}
}
return acc;
}, []);
};
const Demo =()=> {
const [state,setState] = useState({
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [],
allCheckedKeys: [],
selectedKeys: [],
newTreeView: false,
newTreeData: []
});
const onExpand = (expandedKeys) => {
console.log("onExpand", expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setState({
expandedKeys,
autoExpandParent: false
});
};
const onCheck = (checkedKeys, e) => {
const allCheckedKeys = [...checkedKeys, ...e.halfCheckedKeys];
console.log("onCheck", allCheckedKeys);
console.log(createNewTreeData(treeData, allCheckedKeys));
setState((prevState) => ({
...prevState,
allCheckedKeys,
checkedKeys
}));
};
const onSelect = (selectedKeys, info) => {
console.log("onSelect", info);
setState({ selectedKeys });
};
const renderTreeNodes = (data) =>
data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} />;
});
const createTree = () => {
setState((prevState) => ({
...prevState,
newTreeView: true,
newTreeData: createNewTreeData(treeData, prevState.allCheckedKeys)
}));
};
return (
<>
<Tree
checkable
onExpand={onExpand}
expandedKeys={state.expandedKeys}
autoExpandParent={state.autoExpandParent}
onCheck={onCheck}
checkedKeys={state.checkedKeys}
onSelect={onSelect}
selectedKeys={state.selectedKeys}
>
{renderTreeNodes(treeData)}
</Tree>
<button onClick={createTree}>Validate</button>
{state.newTreeView && (
<Tree>{renderTreeNodes(state.newTreeData)}</Tree>
)}
</>
);
}
ReactDOM.render(<Demo />, document.getElementById("container"));

How can change boolean type icons to oposite

I'm want the empty key called archive has the check and with the values "true" has the line icon.
Here is the example: I want the opposite to the column status. Without change the tableIcons.js
Here is the code and codesandbox example:
import React, { useState } from "react";
import { render } from "react-dom";
import MaterialTable from "material-table";
import SaveAltIcon from "#material-ui/icons/SaveAlt";
import tableIcons from "./TableIcons.js";
const rando = max => Math.floor(Math.random() * max);
const rawData = [
{ id: 1111111111, archive: "true", type: "CC" },
{ id: 2222222222, archive: "", type: "RR" },
{ id: 3333333333, archive: "true", type: "CC" },
{ id: 4444444444, archive: "", type: "PS" },
{ id: 5555555555, archive: "true", type: "II" }
];
const columns = [
{ title: "Id", field: "id" },
{ title: "Type", field: "type" },
{ title: "Status", field: "archive", type: "boolean" }
];
const App = () => {
const [data, setData] = useState(rawData);
return (
<MaterialTable
data={data}
columns={columns}
title="Starter Template"
icons={tableIcons}
/>
);
};
render(<App />, document.querySelector("#root"));
https://codesandbox.io/s/material-table-starter-template-0s0np?file=/src/index.js
First of all I modified the archive prop value from "true" to true in order to make conditional checks easier, this way the type is boolean instead of string.
Then I imported Check and Remove icons from the depency you were already using:
import { Check, Remove } from "#material-ui/icons";
And finally where the columns const is defined, I pass a function to the render prop, like this:
render: rowdata => rowdata.archive !== true ? <Check /> : <Remove />
Ofcourse you can change the conditional if needed. Link to the Material-Table documentation on this topic here.
This is your code after the modifications:
import React, { useState } from "react";
import { render } from "react-dom";
import MaterialTable from "material-table";
import SaveAltIcon from "#material-ui/icons/SaveAlt";
import tableIcons from "./TableIcons.js";
import { Check, Remove } from "#material-ui/icons";
const rando = (max) => Math.floor(Math.random() * max);
const rawData = [
{ id: 1111111111, archive: true, type: "CC" },
{ id: 2222222222, archive: "", type: "RR" },
{ id: 3333333333, archive: true, type: "CC" },
{ id: 4444444444, archive: "", type: "PS" },
{ id: 5555555555, archive: true, type: "II" },
];
const columns = [
{ title: "Id", field: "id" },
{ title: "Type", field: "type" },
{
title: "Status",
field: "archive",
type: "boolean",
render: (rowdata) => (rowdata.archive !== true ? <Check /> : <Remove />),
// the ternary expression above is the same as:
// if (rowdata.archive !== true) {
// return <Check />;
// } else {
// return <Remove />;
// }
},
//
];
const App = () => {
const [data, setData] = useState(rawData);
return (
<MaterialTable
data={data}
columns={columns}
title="Starter Template"
icons={tableIcons}
/>
);
};
render(<App />, document.querySelector("#root"));
Here is the link to the sandbox. I hope this works for you!

React-select how to filter out already chosen values?

I have a select component defined like this:
this.state.list = [{label: "test1", value:1}, {label:"test2", value:2}, {label:"test3", value:3}]
this.state.selected = [{label:"test2", value:2}]
let listMap = this.state.list
let list = this.state.list
{listMap.length !== 0 ? listMap.map((item: any, key: number) => (
<div>
<Select
id="list for data"
options={list}
onChange={value => selectList(value)}
placeholder="Select Your Option"
/>
<div/>
))
I want is after test2 is selected, I want the other two drop downs to show test1, and test3.
What I have done so far:
let y = this.state.selected.map(itemY => { return itemY.value })
let x = list.filter(itemX => !yFilter.includes(itemX.value)) // [{value: 1, label:"test1"},{value: 3, label: "test3"}]
And then replacing options property as x.
The filters are working but,
The place holder is not updating the selected values.
What I want to achieve:
And for the next drop down [1], be only able to select those which aren't selected:
You have to separate the three react selects. As you are applying filter on one it will be applicable to all react select.The filtering will also remove in all react select. You can checkout following example.
https://codesandbox.io/s/react-select-5u3rh
import React from "react";
import {
render
} from "react-dom";
import ReactDOM from "react-dom";
import Select from "react-select";
import "react-select/dist/react-select.css";
class ReactSelect extends React.Component {
constructor(props) {
super(props);
this.state = {
itemtitle: "",
multi: true,
multiValue: "eeee...",
options: [{
value: "Color",
label: "Yellow"
},
{
value: "Fruit",
label: "Apple"
},
{
value: "Tool",
label: "Spanner"
}
],
options2: [{
value: "Color",
label: "Yellow"
},
{
value: "Fruit",
label: "Apple"
},
{
value: "Tool",
label: "Spanner"
}
]
};
}
onTitleChange(e, value) {
this.setState({
[e.target.name]: e.target.value
});
this.setState({
multiValue: e.target.value
});
}
handleOnChange(obj) {
this.setState({
multiValue: obj
});
this.setState({
options2: this.state.options2.filter(v => v.value !== obj.value)
})
}
handleOnChange2(obj) {
this.setState({
multiValue2: obj
});
}
render() {
return ( <
div >
<
Select
// multi={this.state.multi}
options = {
this.state.options
}
onChange = {
this.handleOnChange.bind(this)
}
value = {
this.state.multiValue
}
isSearchable = {
false
}
placeholder = "eee" /
>
<
Select
// multi={this.state.multi}
options = {
this.state.options2
}
onChange = {
this.handleOnChange2.bind(this)
}
value = {
this.state.multiValue2
}
isSearchable = {
false
}
placeholder = "eee" /
>
<
/div>
);
}
}
ReactDOM.render( < ReactSelect / > , document.body);
your state should look like this
state = { items : [{value: 1, label:"test1", selected:false},{value: 1, label:"test2", selected:false},{value: 3, label: "test3", selected:false}]
Then when an option is clicked, it sets the key "selected" to true. Afterward, only map the objects with selected false as dropdown items. Remember to use setState.

React.js onChange handler changes all input field when typing.. How do I get each one to type when it is the event target?

I am working on an old app and I am trying to restructure the form that will be used to perform crud on my backend. I decided to make the input reusable, however, I have an issue that when I type into one box I type into all of them, that is, they are all the event target at once! how do I make it so only the input in focus will be typed into?
//update.js
import React, { Component } from "react";
import axios from "../../node_modules/axios";
import Input from "./input";
import "./update.css";
class Update extends Component {
constructor(props) {
super(props);
this.state = {
_id: "",
brand: "",
name: "",
price: "",
imageLink: "",
productLink: "",
category: "",
productType: "",
};
this.handleChange = this.handleChange.bind();
this.onSubmit = this.onSubmit.bind();
}
handleChange = (evt) => {
this.setState({ [evt.target.name]: evt.target.value });
};
onSubmit = (evt) => {
evt.preventDefault();
console.log(this.state);
axios
.put(
`https://makeupinfo.herokuapp.com/product${this.state._id}`,
this.state
)
.then((res) => {
console.log(res);
})
.then((err) => {
console.log(err);
});
};
render() {
const {
_id,
brand,
name,
price,
imageLink,
productLink,
productCategory,
productType,
} = this.state;
const info = [
{ name: "_id", placeholder: "product ID", value: _id },
{ name: "brand", placeholder: "brand", value: brand },
{ name: "name", placeholder: "product name", value: name },
{ name: "price", placeholder: "price", value: price },
{ name: "image-link", placeholder: "image link", value: imageLink },
{ name: "product-link", placeholder: "product link", value: productLink },
{
name: "product category",
placeholder: "product category",
value: productCategory,
},
{ name: "product type", placeholder: "product type", value: productType },
];
return (
<div>
<h2 className='links'>Search by ID and update</h2>
<div className='form-container'>
<Input props={info}></Input>
</div>
</div>
);
}
}
export default Update;
and the child component:
import React from "react";
class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "",
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (e) => {
e.preventDefault();
this.setState({ value: e.target.value });
console.log(this.state.value);
};
render() {
const data = this.props.props;
const dataArray = data.map((item) => (
<input
className='form-control form-control-sm'
name={item.name}
type='text'
placeholder={item.placeholder}
value={this.state.value}
onChange={this.handleChange}
key={item.name}
/>
));
return <div>{dataArray}</div>;
}
}
export default Input;
All your inputs share the same state.value from the Input component. So when you edit one, you edit all of them. Your Input component should only render one input, and you should move info.map to the Update component.
Right now you call map inside Input which generate all the inputs with one shared value. If you do the map in your Update component, you will render one input for each Input, and they will all have their own value, so no more side effect.

Showing error whenever optional props are being sent to a child component

I am trying to optional have row-select features and this should be determined based on a prop that is being passed from parent. I have two grids on a single page, where in one has prop that should enable row selection and the other one does not. But I am getting this error "Cannot read property 'className' of undefined " .
Sandbox: https://codesandbox.io/s/react-table-row-table-alternate-single-row-working-5fr81
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";
import ShowMore from "./ShowMore";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
columns: []
};
}
componentDidMount() {
this.getData();
this.getColumns();
}
getData = () => {
const data = [
{ firstName: "Jack", status: "Submitted", items: [1, 2, 3, 4] },
{ firstName: "Simon", status: "Pending", items: [1, 2] },
{ firstName: "Syls", status: "Pending", items: [1] },
{ firstName: "Pete", status: "Approved", items: [] }
];
this.setState({ data });
};
getColumns = () => {
const columns = [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Status",
accessor: "status"
},
{
Header: "Items",
accessor: "items",
Cell: row => <ShowMore value={row.value} />
}
];
this.setState({ columns });
};
onClickRow = rowInfo => {
this.setState({ allData: rowInfo }, () => {
console.log(this.state.allData);
});
};
render() {
return (
<>
<DataGrid
data={this.state.data}
columns={this.state.columns}
rowClicked={this.onClickRow}
/>
<DataGrid data={this.state.data} columns={this.state.columns} />
</>
);
}
}
In your onRowClick function in DataGrid.js, your function will return nothing on first render (or until something is clicked). ReactTable is expecting SOMETHING here. If you supply an empty object, it will successfully render.

Categories