Why is this function logging undefined when calling it? - javascript

I have a retrieved array of files that have been filtered and put into state.
I can display the files using this:
<List items={this.state.filtPanelMeetFiles.map(file => <div>{file.FileLeafRef}</div>)} onRenderCell={this._onRenderCellFiles} />
and this:
private _onRenderCellFiles = (item) => {
return(
<div>
<tr data-is-scrollable>
<td style={{ width: '150px' }} >{item}</td>
<td style={{ width: '150px' }} >{item.Id}</td>
<td style={{ width: '15px' }}>
<div className={styles.editIcon}><Icon iconName="Delete" id={item.Id} onClick={( this._deleteFile)}/>
</div>
</td>
</tr>
</div>
);
}
I want this function:
public _deleteFile = (ev) => {
const sid = ev;
console.log(sid);
}
To get the id of clicked file, but it's logging undefined. I can see the ID of each file in the array in state but how would I get hold of the ID?
I have identical code in another project but that retrieves items not files. This code works (in the other project) but not in this one. What is different and is the id={item.Id} actually doing anything useful here?
This is what is stored in the filtered state if it helps:

Found it finally!
<div className={styles.editIcon}><Icon iconName="Delete" id={item.Id.toString()} onClick={() => this._deleteFile(item.Id)}/>
<List items={this.state.filtPanelMeetFiles} onRenderCell={this._onRenderCellFiles} />

Related

How to show particular table row detail when it is clicked (Reusable Component) in react

I have created a reusable table component but am facing an issue showing detail for the particular row. what I was doing is if the row id is equal to a particular row id then I was trying to show the detail, but in my case for all rows details are visible.
Codesandbox : reusableTableComponent
what I tried:
const TableCustm = ({ TableHeader, dataVal, selectedRowDetail }) => {
const [selectedTableRow, setSelectedTableRow] = useState(null);
console.log("selectedRowDetail", selectedRowDetail);
console.log("selectedTableRow", selectedTableRow);
const data = dataVal.map((row) => {
const rowData = [];
const keys = Object.keys(row);
keys.forEach((key, index) => {
if (index !== 0) {
rowData.push({
key: TableHeader[index],
val: row[key]
});
}
});
return (
<>
<tr onClick={() => setSelectedTableRow(row)}>
{rowData.map((i) => (
<td className="font-lato text-[14px] text-p_black font-semibold py-0">
<div className="d-flex py-2">{i.val}</div>
</td>
))}
</tr>
// **********************detail table Row ********************
<tr>
<td colspan={TableHeader.length}>
<div style={{ background: "#dcdcdc", padding: "20px" }}>
<button className="btn btn-primary">clickme</button>
<hr className="my-2" />
<div className="d-flex ">row detail</div>
</div>
</td>
</tr>
// *******************end detail
</>
);
});
return (
<Table responsive borderless>
<thead>
<tr>
{TableHeader.map((item) => (
<th key={item.id} className="font-normal">
<div className="flex py-[15px]">{item.label}</div>
</th>
))}
</tr>
</thead>
<tbody className="border-0">{data}</tbody>
</Table>
);
};
I can see what you're trying to do. I'm by no means a react specialist, but nothing on Netflix was gripping me, so took up your challenge.
You're almost there, just a few things that are happening that are getting in your way.
I've got this working on codesandbox: https://codesandbox.io/embed/goofy-tereshkova-66p1ki?fontsize=14&hidenavigation=1&theme=dark
1) React is re-rendering the App Component when you click on the row
I'm not sure the best way to get around this, but every time you click the row (even with my step 2), it will re-generate the UUID. I got around this by just hard coding the IDs, as I assume you'll have a better way for generating IDs (or will need to figure out a way to stop the reloading.
But, for now, hardcode the id so you can follow step 2
const testData = [
{
id: 1,
2) Your use of useState between the Parent (App) and Child (TableCustm) components.
Not sure if this is intentional, but you're duplicating selectedTableRow state in both components. What I think you should do is hold the state in the parent (App) component, but pass both the state and the setState method to the child component inside of app.js like so:
<TableCustm
TableHeader={TableHeader}
dataVal={dataValue}
selectedRowDetail={selectedRow}
setRow={setSelectedRow}
/>
So now, inside the child component (TableCustom.js) you can set the state of the selected row like so:
<tr
onClick={(i) => {
setRow(row.id);
}}
>
And then, becaue you're also passing down (from the Parent to Child component) the current selected row selectedRowDetail, you can then conditionally render the row on the screen.
{row.id === selectedRowDetail &&
<tr>
<td colspan={TableHeader.length}>
<div style={{ background: "#dcdcdc", padding: "20px" }}>
<button className="btn btn-primary">clickme</button>
<hr className="my-2" />
<div className="d-flex ">row detail</div>
</div>
</td>
</tr>
}
Also, you might want to add a conditional step when setting the state of the selected row to null, so when you click again it disappears:
<tr
onClick={(i) => {
if (row.id === selectedRowDetail) {
setRow(null);
} else {
setRow(row.id);
}
}}
>
Hope that helps!
When you are working with React you have to understand when to use state and when to use props.
In your scenario you have two approaches:
When you want to show many details at same time, each row manage it owns state.
When you want to show one detail at a time, you must likely want to the parent Table component to manage your state.
It seems you want the approach 2) show one detail at a time, so you have to show it based on the selected row with selectedTableRow === row:
import React, { useState } from "react";
import { Table } from "react-bootstrap";
const TableCustm = ({ TableHeader, dataVal, selectedRowDetail }) => {
const [selectedTableRow, setSelectedTableRow] = useState(null);
console.log("selectedRowDetail", selectedRowDetail);
console.log("selectedTableRow", selectedTableRow);
const data = dataVal.map((row) => {
const rowData = [];
const keys = Object.keys(row);
keys.forEach((key, index) => {
if (index !== 0) {
rowData.push({
key: TableHeader[index],
val: row[key]
});
}
});
return (
<>
<tr
onClick={() =>
setSelectedTableRow(selectedTableRow === row ? null : row)
}
>
{rowData.map((i) => (
<td className="font-lato text-[14px] text-p_black font-semibold py-0">
<div className="d-flex py-2">{i.val}</div>
</td>
))}
</tr>
{selectedTableRow === row && (
<tr>
<td colspan={TableHeader.length}>
<div style={{ background: "#dcdcdc", padding: "20px" }}>
<button className="btn btn-primary">clickme</button>
<hr className="my-2" />
<div className="d-flex ">row detail</div>
</div>
</td>
</tr>
)}
</>
);
});
return (
<Table responsive borderless>
<thead>
<tr>
{TableHeader.map((item) => (
<th key={item.id} className="font-normal">
<div className="flex py-[15px]">{item.label}</div>
</th>
))}
</tr>
</thead>
<tbody className="border-0">{data} </tbody>
</Table>
);
};
export default TableCustm;
CodeSandbox: https://codesandbox.io/s/epic-mcnulty-g0hqhw?file=/src/TableCustm.js
PS: I believe your code need refactoring and I highly recommend you the Code With Mosh videos to start working with React: https://www.youtube.com/watch?v=Ke90Tje7VS0
Refactored code (not ideal yet, but better):
import React, { useState } from "react";
import { Table } from "react-bootstrap";
const TableRow = ({ data, onClickRow, showDetails }) => {
return (
<>
<tr onClick={onClickRow}>
{data.map((item, i) => (
<td
key={i}
className="font-lato text-[14px] text-p_black font-semibold py-0"
>
<div className="d-flex py-2">{item.val}</div>
</td>
))}
</tr>
{showDetails && (
<tr>
<td colSpan={data.length}>
<div style={{ background: "#dcdcdc", padding: "20px" }}>
<button className="btn btn-primary">clickme</button>
<hr className="my-2" />
<div className="d-flex ">row detail</div>
</div>
</td>
</tr>
)}
</>
);
};
const TableCustm2 = ({ TableHeader, dataVal }) => {
const [selectedTableRow, setSelectedTableRow] = useState(null);
return (
<Table responsive borderless>
<thead>
<tr>
{TableHeader.map((item) => (
<th key={item.id} className="font-normal">
<div className="flex py-[15px]">{item.label}</div>
</th>
))}
</tr>
</thead>
<tbody className="border-0">
{dataVal.map((row, index) => (
<TableRow
key={index}
data={TableHeader.map((key) => ({
key: key.label,
val: row[key.label]
}))}
onClickRow={() => {
setSelectedTableRow(index);
}}
showDetails={selectedTableRow === index}
/>
))}
</tbody>
</Table>
);
};
export default TableCustm2;

Displaying selected options in custom multiselect dropdwown as tags - React

I've custom multiselect dropdown which is implemented as given below:
const RequestExpertSupport = function Basic({
data,
onSelectedItemsChanged,
selectedItemsIndices,
}) {
const props = {
data: [
"My account and profile",
"Understanding my footprint",
"Target setting",
"Specific actions",
"Creating a plan",
"Finance, funding and grants ",
"Using the Carbon Planner platform",
"Other",
],
selectedItemsIndices: [],
};
//
const handleSelectedItemsChanged = useCallback(
selectedItemsIndices => {
onSelectedItemsChanged(selectedItemsIndices);
},
[onSelectedItemsChanged]
);
function onSelectedItemsChanged(selectedItemsIndices) {
console.log(selectedItemsIndices);
}
function renderItem(datum, index) {
return <span>{datum}</span>;
}
return (
<RequestExpertSupportDiv>
<div className="container">
<div className="formContainer">
<form>
<label className="helpLabel">What would you like help with?</label>
<DropdownListTrigger
dropdownList={
<MultiSelectDropdownList
id="multiSelectDrop"
data={props.data}
onSelectedItemsChanged={handleSelectedItemsChanged}
renderItem={renderItem}
selectedItemsIndices={selectedItemsIndices}
/>
}
position="right"
className="ddlFilter"
>
<button className="zb-button zb-button-secondary zb-button-with-icon-after">
<span>Choose topic(s)</span>
<Icon
name="chev-down-xsmall"
style={{
verticalAlign: "text-bottom",
}}
title={null}
/>
</button>
</DropdownListTrigger>
<div className="selectedTopics">Selected topics are:</div>
<label className="tellUsLabel">What would you like help with?</label>
<textarea
name="helpReview"
rows="4"
cols="43"
className="textArea"
style={{ width: "410px", height: "290px", marginTop: "2%" }}
placeholder="Type your message here ..."
></textarea>
<button className="sendBtn" name="sendBtn">
Send
</button>
</form>
</div>
</div>
</RequestExpertSupportDiv>
);
};
export default RequestExpertSupport;
This code fetches the indices of selected options in Multiselect dropdown.
function onSelectedItemsChanged(selectedItemsIndices) {
console.log(selectedItemsIndices);
}
Console is given below:
Now I want to display those selected options as tags like this:
This is the code for tags:
<Div
flex="flex"
display="inline-flex"
height="36px"
borderWidth="1px solid #009FAC"
borderRadius="3px"
backgroundColor="#def8fa"
justifyContent="space-around"
alignItems="center"
marginRight="10px"
marginBottom="10px"
style={{ minWidth: "92px", maxWidth: "175px" }}
>
<span
padding="0px"
fontSize="14px"
lineHeight="20px"
fontWeight="400"
marginLeft="5px"
marginRight="5px"
style={{ maxWidth: "142px" }}
>
// need to put selected options here
</span>
<img name="close-inverted-small" onClick={event => removeSelectedTopic(event, topicId)} />
</Div>
I'm not getting how to link function SelectedItemsChanged(selectedItemsIndices) with that tags frontend code to dosplay selected options as tags...
You need to put all the codes in the last part in a loop for rendering data. for example you should write last part as below by using selectedItemIncedies and data:
{ selectedItemIncedies.map ((selectedItemIndex) => { return (<Div
flex="flex"
display="inline-flex"
height="36px"
borderWidth="1px solid #009FAC"
borderRadius="3px"
backgroundColor="#def8fa"
justifyContent="space-around"
alignItems="center"
marginRight="10px"
marginBottom="10px"
style={{ minWidth: "92px", maxWidth: "175px" }}
<span
padding="0px"
fontSize="14px"
lineHeight="20px"
fontWeight="400"
marginLeft="5px"
marginRight="5px"
style={{ maxWidth: "142px" }}
>
{data.find((item, index)=> index===selectedItemIndex)}
</span>
<img name="close-inverted-small" onClick={event => removeSelectedTopic(event, topicId)} />
</Div>)})}
You'll want to render the tags using map based on the selectedItemsIndices array. First, pass the selectedItemsIndices array as props to a TagListComponent.
Once that's all set, render each of the selected tags in selectedItemsIndices in the TagListComponent
TagListComponent.js
const TagListComponent = function Basic({
selectedItemsIndices,
}) {
return (
<div>
{selectedItemsIndices.map((selectedItemIndex, index) => {
return (
<div>
<span>{data[selectedItemIndex]}</span>
</div>
);
})}
</div>
);
};
export default TagListComponent;
In the <span/> tag above, put whatever styling you'd like for the tag itself.

React Lightbox Component doesn't open

I admit I am very new to React and still getting my head around things, but I am using the react-lightbox-component to display images in a grid and that works just fine but the onClick event doesn't do anything then it calls the toggleLightBox function.
I know the onClick is working as I tested it against a simple console.log, what am I missing to be able to display the image in a lightbox when clicked?
const App = ({ compile }) => (
<div style={{margin: 20}}>
<header style={{textAlign: "center"}}>
<h1><Words animate>Art</Words></h1>
</header>
<body>
<h3>Android</h3>
</body>
<Lightbox
images={androidimages}
renderImageFunc={(idx, image, toggleLightbox, width, height) => {
return (
<img
key={idx}
src={image.src}
style={{width: '150px', height: '200px', margin: '20px'}}
onClick={
toggleLightbox.bind(null, idx)
}
/>
)
}
}
/>
</div>
);

React Carousel with text

I am using https://www.npmjs.com/package/react-multi-carousel in one of my react project and I have implemented it and it works fine.
I am now in the need to implement the text (legend) over the carousel exactly like https://codesandbox.io/s/lp602ljjj7 which uses another package but I need that scenario and not that package because my need is different (Using nextjs so multi-carousel supports ssr and hence using it).
My only need is need to know how to implement the legend text over the carousel image using react-multi-carousel.
My code example: https://codesandbox.io/s/react-multi-carousel-playground-bt3v7
Change the structure of the return statement to the following structure.
Then you can store the value of the legend in the image array and pass the value to the p tag
const Simple = ({ deviceType }) => {
return (
<Carousel
ssr
partialVisbile
deviceType={deviceType}
itemClass="image-item"
responsive={responsive}
>
{images.slice(0, 5).map((image, index) => {
return (
<div key={index} style={{ position: "relative" }}>
<img
draggable={false}
alt="text"
style={{ width: "100%", height: "100%" }}
src={image}
/>
<p
style={{
position: "absolute",
left: "50%",
bottom: 0,
color: "white",
transform: " translateX(-50%)"
}}
>
Legend:{index}.
</p>
</div>
);
})}
</Carousel>
);
};
export default Simple;
Live demo
You should change array structure like below.
const images = [
{
image: "https://images.unsplash.com/photo-1549989476-69a92fa57c36?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60",
text: "First image",
}
];
Then inside loop just add text and style it our way!
const Simple = ({ deviceType }) => {
return (
<Carousel
ssr
partialVisbile
deviceType={deviceType}
itemClass="image-item"
responsive={responsive}
>
{images.map(image => {
return (
<div>
<img
draggable={false}
alt="text"
style={{ width: "100%", height: "100%" }}
src={image.image}
/>
{/* Have to style so this should see over the image */}
<span>{image.text}</span>
</div>
);
})}
</Carousel>
);
};

How do I pass props from child to parent in React?

I have a dynamic table that renders a list of items that the user selects. The user can select an item from a drop down menu and then add that item to the list. As of right now I store the item list in the state of my component, which allows the table to render dynamically. What I would like is for the user to be able to click on an item in the table and be able to edit certain parts of that item, such as the quantity that they are selecting. Once the user clicks on that item from the table a Modal will appear that gets filled with the information from that specific item. My problem is that within the modal, when the user changes say the quantity of that item, I would like the Modal to close and then update the table with that value that the user changed.
Is there a way to pass this updated list of items back to the parent? Or is this not viable? and if so, what would be the right way to go about this. I will post my code below so that you guys can get a better understanding about what I'm trying to accomplish.
NOTE My modal isn't complete but I just would like to know how I can pass props back to the parent component.
Parent.js
export default Parent extends React.Component{
constructor(props){
super(props);
this.state = {
ItemList = [],
IDClicked = "",
}
AddItemHandler(){
...stuff to add to ItemList
}
RenderModal(){
let itemList = this.state.ItemList
<ItemModal items={itemList} />
}
RowClick(e){
//console.log(e.target.id)
var items = this.state.ItemList;
for(let i = 0; i < items.length; i++){
if(items[i].ID == e.target.id){
var Item = {
ID: items[i].ID,
PartName: items[i].PartName,
Quantity: items[i].Quantity
}
}
}
//console.log("Item clicked: " + JSON.stringify(Item));
this.setState({IDClicked: e.target.id})
(document.getElementById('myModal')).style.display = "block";
}
RenderTable(items){
var rows = [];
for(let i = 0; i < items.length; i++){
rows.push(
<tr style={{backgroundColor: '#B7BCDF'}} id={items[i].ID} key={i}>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].PartName}
</td>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].Description}
</td>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].Type}
</td>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].Quantity}
</td>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].Units}
</td>
</tr>
)
}
return (
<div className="TableScroll2" style={{width: '99.5%', height: 'none'}}>
<table className="TableRows">
<tbody>
{rows}
</tbody>
</table>
</div>
);
}
render(){
return(
<div id="mymodal">
this.RenderModal();
</div>
<div style={{width: '50%', marginLeft: 'auto', marginRight: 'auto'}}>
<div className="ParTableContainer" style={{marginTop: '5%'}}>
<table className="PartTableHeaderContainer" style={{textAlign: 'center'}}>
<thead>
<tr>
<th style={{width: '20%'}}>Part Name</th>
<th style={{width: '20%'}}>Description</th>
<th style={{width: '20%'}}>Type</th>
<th style={{width: '20%'}}>QTY</th>
<th style={{width: '20%'}}>U/M</th>
</tr>
</thead>
</table>
</div>
{this.RenderTable(this.state.ItemList)}
</div>
<button style={{marginTop: '2%', marginBottom: '5%'}} onClick={this.AddItemHandler.bind(this)}>Add Item</button>
}
}
You cannot pass props from a child to the parent. There are however ways for a child to communicate with it's parent, which could be used to solve your problem.
The way to go is usually to use callbacks - pass a function from your parent to your child which the child can call to update the state of the parent. Here is an example that updates the parent state:
function Parent() {
const [counter, setCounter] = useState(0)
return (
<div>
Current: {state}
<Child increment={() => {
setCounter(current => current + 1)
}}}/>
</div>
)
}
function Child(props) {
return <button onClick={props.increment}>Click me</button>
}
(This example was done using hooks, which I strongly recommend learning)
Here it is without hooks:
class Parent extends Component {
constructor() {
this.state = { counter: 0 }
}
render() {
return (
<Child increment={() => {
this.setState((current) => {
return { counter: current.counter + 1 }
})
}}}/>
)
}
}
function Child(props) {
return <button onClick={props.increment}>Click me</button>
}

Categories