How to pass data from a table row and pass it to another component when it renders? - javascript

I have a component that shows a table and one of its columns have a field Actions which has buttons (view, edit, delete etc.). On button click, I need to render another component (component is a popup) and pass the data from the table so that it displays the data in some form which I need to further add.
I have managed to get the current data from its row by passing in onClick. I tried to use state for another component to render but it didn't work out. I'm using Semantic-UI React components to display the button with animations.
Here is the code that has the table,
const MainContent = () => {
const [actions, setActions] = useState(false);
const handleView = (rowData) => {
console.log(rowData);
setActions(true);
if (actions == true) return <ParentView />;
};
....
....
const contents = (item, index) => {
return item.routeChildEntry ? (
<>
<tr key={index}>
<td>{item.appName}</td>
<td></td>
<td>{item.createdTs}</td>
<td>{item.pattern}</td>
<td></td>
<td></td>
<td></td>
<td>
<Button animated onClick={() => handleView(item)}>
<Button.Content visible>View</Button.Content>
<Button.Content hidden>
<Icon name="eye" />
</Button.Content>
</Button>
</td>
</tr>
{item.routeChildEntry.map(routeContents)}
</>
) : (
....
....
....
);
};
return (
<div>
....
{loading ? (
<div className="table-responsive">
<table className="table">
<thead>
<tr>
<th>AppName</th>
<th>Parent_App</th>
<th>Created_Date</th>
<th>Req_Path</th>
<th>Resp_Code</th>
<th>Resp_Content_Type</th>
<th>Resp_Delay</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{data.map((routes, index) => {
return routes.map(contents, index);
})}
</tbody>
</table>
</div>
) : (
....
....
)}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default MainContent;
Below is the component to render on button click,
import React from "react";
import Popup from "reactjs-popup";
import { Icon } from "semantic-ui-react";
const Parent = (props) => {
return (
<Popup trigger={<Icon link name="eye" />} modal closeOnDocumentClick>
<h4>in parent</h4>
</Popup>
);
};
export default Parent;
How can I render the other component and pass data to it on button click?

Data can be passed to other components as props.
For example, if your component is <ParentView />, and the data you are passing is contained in the variable rowData, you can pass it as:
<ParentView dataPassedByAkhil = {rowData}/>
Then in your ParentView component,
export default function ParentView({dataPassedByAkhil}) {
console.log(dataPassedByAkhil);
Alternatively, you can accept the data as props as below
export default function ParentView(props) {
console.log(props.dataPassedByAkhil);
If you want to open and close another popup, you can pass a state just like above.
<PopupComponent open={stateSetForPopupByParent}/>
Example of popup using state.
Updated link above with how to pass data to the dialog from rows of buttons
Here is the full code:
export default function FormDialog() {
const [open, setOpen] = React.useState(false);
const [valueofRow, setValueOfRow] = React.useState();
const handleClickOpen = (value) => {
setValueOfRow(value);
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button
variant="outlined"
color="primary"
onClick={() => {
handleClickOpen("John");
}}
>
Row 1 - value is John
</Button>
<br />
<br />
<Button
variant="outlined"
color="primary"
onClick={() => {
handleClickOpen("Sally");
}}
>
Row 2 Value is Sally
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="edit-apartment"
>
<DialogTitle id="edit-apartment">Edit</DialogTitle>
<DialogContent>
<DialogContentText>Dialog fired using state</DialogContentText>
<h1>{valueofRow} was clicked and passed from the row</h1>
<TextField
autoFocus
margin="dense"
id="field"
label="some field"
type="text"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="secondary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
</div>
);
}

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;

React: How to extract values from Modals?

I have a Chakra UI Modal which opens up onClick of a button. I want to be able to extract the values that a user puts into the inputs/radio buttons when they close the Modal. The Modal class and the Modal/Button render is shown below. Since the input and radio buttons are defined within the Modal class, is it possible to get their final values onClose?
Modal.tsx
import React from 'react'
import {
Modal as ChakraModal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
RadioGroup,
Stack,
Radio,
VStack
} from "#chakra-ui/react"
import Button from './Button'
import Input from './Input'
type Props = { isOpen : boolean } & { onClose : () => void} & { label : string }
const Modal = ({ label, isOpen, onClose, ...rest }: Props) => (
<ChakraModal {...rest} isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>{label}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack spacing={4}>
<RadioGroup>
<Stack direction="row">
<Radio value="1">Annually</Radio>
<Radio value="2">Semi-Annual</Radio>
<Radio value="3">Quarterly</Radio>
<Radio value="4">Monthly</Radio>
</Stack>
</RadioGroup>
<Input
label="Custom Interest rate"
name="Custom Interest rate"
/>
</VStack>
</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Save
</Button>
</ModalFooter>
</ModalContent>
</ChakraModal>
)
export default Modal
Render
<Button onClick={onOpen}> Settings </Button>
<Modal
label="Custom Settings"
isOpen={isOpen}
onClose={onClose}
/>
You should probably send a state as the modal's prop.
const [state, setState] = React.useState();
<Modal
label="Custom Settings"
isOpen={isOpen}
onClose={onClose}
onChange={setState}
/>
To get the type of setState, hover over the variable and you can see the type definition.

Rendering multiple componets in a table react

I have a component that renders a table with data from a database, I am experiencing a problem where I cant render the second component called EditButton bottom of the code, I managed to render the 1st component DeleteButton bottom of the code, but when I try to render another button I get errors. The goal is to render another button in the table. Thanks in advance :)
import React, { useState } from 'react';
import { Button } from 'semantic-ui-react';
import { currentloginid } from '../login/loginid.js';
import { deletequestion } from '../question/deletequestion.js';
import { editanswer } from '../answer/editanswer.js';
export const ViewQuestionComponent = () => {
let [state, setState] = useState([]);
const handleViewQuestion = async () => {
try {
const response = await fetch('http://localhost/gotaquestion/api/api.php?action=viewquestion', {
method: 'GET',
credentials: 'include'
});
const data = await response.json();
const result = await data;
setState(data);
} catch (e) {
console.log(e);
}
}
return (
<div>
<ViewQuestion onClick={handleViewQuestion} />
<div id="questions">
<Table rows={state}>
<DeleteButton onClick={deletequestion} />
</Table>
</div>
</div>
);
};
export function ViewQuestion({onClick}) {
return (
<Button onClick={onClick}>View Question</Button>
);
}
export default ViewQuestion;
const Table = ({ rows, setIdTobeDeleted, children }) => (
<table className="ui single line table">
<tbody>
{rows.map((row) => (
<tr key={row.questionid}>
<td>{row.question}</td>
<td>{row.timestamp}</td>
<td>{row.catagories}</td>
<td>{row.answer === null ? "Not Answered" : row.answer}</td>
<td>
{React.cloneElement(children, { questionid: row.questionid })}
</td>
<td>
//I want the EditButton to render there instead on DeleteButton
{React.cloneElement(children, { questionid: row.questionid })}
</td>
<td>{row.questionid}</td>
</tr>
))}
</tbody>
</table>
);
const EditButton = ({ questionid, onClick }) => (
<button
className="ui negative basic button"
onClick={() => onClick(editanswer)}
>Edit Question</button>
);
const DeleteButton = ({ questionid, onClick }) => (
<button
className="ui negative basic button"
onClick={() => onClick(questionid)}
>Delete Question</button>
);
I think in your view you have missed the edit button.
Please consider changing
return (
<div>
<ViewQuestion onClick={handleViewQuestion} />
<div id="questions">
<Table rows={state}>
<DeleteButton onClick={deletequestion} />
</Table>
</div>
</div>
);
to
return (
<div>
<ViewQuestion onClick={handleViewQuestion} />
<div id="questions">
<Table rows={state}>
<DeleteButton onClick={deletequestion} />
</Table>
<Table rows={state}>
<EditButton onClick={editanswer} />
</Table>
</div>
</div>
);

How to tigger onClick function of another component element

I created one class base component and one functional component. There are three buttons inside the functional component and I called that component to my class based component.
The functional component:
function PanelButton(props){
return (
<div>
<Button.Ripple
color="success"
type="submit"
style={{margin:"5px", width:"110px"}}
>
Submit
</Button.Ripple>
<Button.Ripple
color="primary"
id="clearButton"
type="button"
style={{margin:"5px", width:"110px"}}
>
Clear
</Button.Ripple>
<Button.Ripple color="danger" type="button" style={{margin:"5px", width:"110px"}}>
Close
</Button.Ripple>
</div>
)
}
export default PanelButton;
The class base component, in which I imported the functional component into:
import PanelButton from '../../components/customzied/PanelButton';
class TicketNew extends React.Component{
state = {
alertOption:[],
}
clickClear = () => {
console.log("ok");
}
render() {
const rqst = this.state.rquirdSate;
return (
<Card>
<Formik>
{ ({ errors, touched}) => (
<div>
<Form onSubmit={handleSubmit}>
<CardHeader>
<PanelButton />
</CardHeader>
<CardBody>
<Row />
</CardBody>
</Form>
</div>
)}
</Formik>
</Card>
)
}
}
export default TicketNew;
When I click the button(id = "clearButton") from functional component, I need to run the Click clear function in Class component.
You can pass onClick callback handlers as props to PanelButton to attach to each button's onClick prop. Pass clickClear as callback for clear button.
PanelButton
function PanelButton(props) {
return (
<div>
...
<Button.Ripple
color="primary"
id="clearButton"
type="button"
style={{ margin: "5px", width: "110px" }}
onClick={props.onClear} // <-- attach callback to button's onClick handler
>
Clear
</Button.Ripple>
...
</div>
);
}
TicketNew
class TicketNew extends React.Component {
state = {
alertOption: []
};
clickClear = () => {
console.log("ok");
};
render() {
const rqst = this.state.rquirdSate;
return (
<Card>
<Formik>
{({ errors, touched }) => (
<div>
<Form onSubmit={handleSubmit}>
<CardHeader>
<PanelButton onClear={this.clickClear}/> // <-- pass this.clickClear to onClear prop
</CardHeader>
<CardBody>
<Row></Row>
</CardBody>
</Form>
</div>
)}
</Formik>
</Card>
);
}
}
You can pass the clickClear function as props to the PanelButton component. The PanelButton code will look like the following,
function PanelButton(props){
return(
<div>
<Button.Ripple color="success" type="submit" style={{margin:"5px", width:"110px"}}>
Submit
</Button.Ripple>
<Button.Ripple color="primary" id="clearButton" onClick={props.onClickCallback} type="button" style={{margin:"5px", width:"110px"}}>
Clear
</Button.Ripple>
<Button.Ripple color="danger" type="button" style={{margin:"5px", width:"110px"}}>
Close
</Button.Ripple>
</div>
)
}
And the TicketNew code will look like this,
...
<CardHeader>
<PanelButton onClickCallback={this.clickClear.bind(this)} />
</CardHeader>
...

How to implement conditional rendering in React Reusable Component?

I have this reusable component where I would like to pass a value through that if true: a button is returned and if the value is false: the button doesn't render. I can't figure out how to do this using reusable components. The boolean variable I want to use is displayButton where if it is true, then the button is rendered and if it is false then the button is not rendered.
const Test = ({ name, value, onClick, purchased, displayButton }) => {
return (
<div class="cardData">
<h5>{name}</h5>
<p>Cost: {value}</p>
//if display button = true, display this button, if false, button is not displayed
<button
type="button"
onClick={onClick}
class="btn btn-primary"
value={value}
name={name}
>
Purchase
</button>
<h4>{purchased}</h4>
</div>
);
};
export default Test;
Any help would very appreciated!
const Test = ({ name, value, onClick, purchased, displayButton }) => {
return (
<div class="cardData">
<h5>{name}</h5>
<p>Cost: {value}</p>
{displayButton &&
<button
type="button"
onClick={onClick}
class="btn btn-primary"
value={value}
name={name}
>
Purchase
</button>
}
<h4>{purchased}</h4>
</div>
);
};
export default Test;
if displayButton has the value of true it will render the button otherwise not
This will work.
const Test = ({ name, value, onClick, purchased, displayButton }) => {
return (
<div class="cardData">
<h5>{name}</h5>
<p>Cost: {value}</p>
{ displayButton &&
<button
type="button"
onClick={onClick}
class="btn btn-primary"
value={value}
name={name}
>
Purchase
</button>
}
<h4>{purchased}</h4>
</div>
);
};
export default Test;
You can create a ShowHide component which can be utilized for conditional-rendering instead of having logical operators in every JSX where you need to conditionally render.
// create a ShowHide component
const ShowHide = ({show,children} ) => show && children
// your Test component with the button wrapped in ShowHide
const Test = ({ name, value, onClick, purchased, displayButton }) => {
return (
<div class="cardData card col-6 m-3">
<h5>{name}</h5>
<p>Cost: {value}</p>
<ShowHide show={displayButton}>
<button
type="button"
onClick={onClick}
class="btn btn-primary"
value={value}
name={name}
>
Purchase
</button>
</ShowHide>
<h4>{purchased}</h4>
</div>
);
};
const root = document.getElementById('root');
ReactDOM.render(
<Test name="My Card"
value="250,000"
onClick={() => alert('clicked')}
purchased="Yes!"
displayButton={false}
/>,
root
)
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Good Luck...

Categories