How to select data that i want when click button (React JS) - javascript

i have this example data, when i click button show more it will show popup (using Modal reactbootstrap) and i will show more detail like ID,Name,Age,City,Number,Address,Education and many more.. how i can select and get all data in popup only when i click button 'show more'
and this my code
import React from "react";
import MUIDataTable from "mui-datatables";
import axios from "axios";
import { Modal, Button } from "react-bootstrap";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
errors: null,
isLoading: true,
};
}
get = async () => {
const option = {
url: "/api/url",
method: 'POST',
headers: {
"Access-Control-Allow-Origin": "*"
},
data: {
"data": {
"data": "........."
},
"encrypt": 0
}
};
axios(option)
.then(response => {
const tableData = response.data.data.map(post => {
const {ID,Name,Age,City,Number,Address,Education} = post;
const Popup = () => {
const [lgShow, setLgShow] = React.useState(false);
const [isOpen, setIsOpen] = React.useState(false);
const showModal = () => {
setIsOpen(true);
};
const hideModal1 = () => {
setIsOpen1(false);
};
return (
<div>
<Button onClick={() => setLgShow(true)}>Show more</Button>
<Modal
size="lg"
show={lgShow}
onHide={() => setLgShow(false)}
aria-labelledby="example-modal-sizes-title-lg"
>
<Modal.Header closeButton class="modal-dialog modal-lg">
<Modal.Title id="example-modal-sizes-title-lg">
Data {nama_loket}
</Modal.Title>
</Modal.Header>
<Modal.Body>
Detail<br/>
<div><b> ID</b></div><br />
<div>{ID}</div><br />
<div><b>Name</b></div><br />
<div >{Name}</div><br />
<div><b>Age</b></div><br />
<div>{Age}</div><br />
<div><b>City</b></div><br />
<div>{City}</div><br />
<div><b>Number</b></div><br />
<div>{Number}</div><br />
<div><b>Adress</b></div><br />
<div>{Address}</div><br />
<div><b>Educaton</b></div><br />
<div>{Education}</div><br />
</Modal.Body>
</Modal>
</div>
);
};
return [
[ID],
[Name],
[Age],
[City],
[Number],
<Popup></Popup>
];
});
this.setState({
data: tableData,
isLoading: false
});
console.log(response.data.data);
console.log(this.state.data)
})
// If we catch any errors connecting, let's update accordingly
.catch(error => {
console.log(error.response);
this.setState({ error, isLoading: false })
}
);
}
componentDidMount() {
this.get();
}
render() {
const { isLoading} = this.state;
const columns = ["ID", "Name", "Age", "City", "Phone Number",""];
const options = {
filterType: "dropdown",
responsive: "scroll",
selectableRows:false,
};
return (
<div>
{!isLoading ? (
<MUIDataTable
data={this.state.data}
columns={columns}
options={options}
/>)
: (
<p>Loading...</p>
)}
</div>
);
}
}
export default App
how i get data in my popup when i click. example i have 5 row, when i click second row, data will selected and get is only the second data in second row.. can anyone help me?

You can do this by saving the row clicked in the state and then using the state to show it on the modal, just like you are doing to show your modal.
<Button
onClick={() => {
setLgShow(true)
setSelectedPost(post)
}}
>
Show more
</Button>
The idea is to have one button for each post, but you don't need to render the Modal more than once, so render the Modal outside of the response.data.data.map and use the state saved on the selectedPost to show the data inside the Modal.

I think it's better to create the table manually using css https://www.w3schools.com/css/css_table.asp
And for the rows just use React mapping https://reactjs.org/docs/lists-and-keys.html
Then create a button inside your mapping that call a function to open the modal.
Create a new state called displayedData to store the row that you want to display. Inside your render:
{data.map((value, index) => {
<tr key={index}>
<td>{value.id}</td>
<td>{value.name}</td>
. . .
<td>{value.phone}</td>
<td> <button onClick={()=> this.OpenData(value)}>Show More</button> </td>
</tr>
})}
and for the OpenData function:
async OpenData(value){
await this.setState({displayedData : value})
this.openModal()
}
Last, just use displayedData state to display your data inside the modal.
edit:
Also, move your modal from your axios fetch, just create its own function. use your axios fetch just to update your data state

Related

How to Pass an index / value to different cards in react. Only last item appears

In Short, Let's assume that I have a list of items that I am displaying, and on each item there is a button, like a Quick View button to view extra information about the product in a modal pop-up. Now, everything is working perfectly, except when I click on the button, it brings the information related to the last item of my API/JSON file .. whatever the source of the file is. So, the last item in the Array. when I console.log(index)
it brings the correct index for each card, but it doesn't show the information about each product separately.
import React, { Component } from "react";
import { productsInfo } from "./ProductsData"; // the JS file containing the data
import Modal from "react-modal";
import "./Products.css";
import ProductDetailsPopUp from "./ProductDetailsPopUp";
import axios from "axios";
Modal.setAppElement("#root");
export default class Products extends Component {
state = {
productsData: [],
modalIsOpen: false,
};
OpenModal = () => {
this.setState({ modalIsOpen: true });
};
CloseModal = () => {
this.setState({ modalIsOpen: false });
};
changeProduct = (item, index) => {
this.setState({ showProduct: item });
};
render() {
// const {id, img, title, price, isNew, country, currency} = productsInfo
return (
<div className="ProductsContainer">
{productsInfo.map((item, index) => {
return (
<div className="cardHolder" key={item.id}>
<img src={item.img} alt="Products" />
<button
onClick={() => {
this.OpenModal();
console.log(index);
}}
className="MainProductBtn"
>
QUICK VIEW
</button>
<Modal
key={index}
className="mainModal"
style={{
overlay: {
backgroundColor: "#3333",
opacity: 0.4,
transition: "0.4s",
},
}}
isOpen={this.state.modalIsOpen}
onRequestClose={this.CloseModal}
>
<div className="popupHeader" key={item.id}>
<h3>{item.title}</h3>
<button onClick={this.CloseModal}>×</button>
</div>
<ProductDetailsPopUp />
</Modal>
<p>{item.title}</p>
<small>{`${item.price} USD`}</small>
</div>
);
})}
</div>
);
}
}
I tried including the index in onClick function and also passing it in the state, didn't work
You create Modal for every item inside your map method meaning that it will display productsInfo.length modals and the last one will be on top.
Remote Modal tag from the map method and onClick set the current item at your state, change the display of the dialog to true and read the current item from the state inside your Modal.
e.g.
state = {
productsData: [],
modalIsOpen: false,
currentItem: null
};
OpenModal = item => {
this.setState({ modalIsOpen: true, currentItem: item });
};
onClick={() => {
this.OpenModal(item);
console.log(index);
}}

How do I sync tests in React?

I have CommentsList component which displays a list with all the comments. Each comment has a 'Reply to' button that opens the AddComment component (this component allows me to add a reply to a comment). To display the AddComment component for each comment, I used an array of states.
The AddComment component contains a text area for the input, a cancel button and a submit button. When I click on the submit button and the reply is added successfully, the AddComment component closes. If the input is empty and I click on the submit button, the component doesn't close because the input can't be empty in order to be submitted successfully.
I want to test this functionality so that I can verify if the AddComment component disappears after I post a reply.
The problem is that in tests my AddComment component doesn't disappear when I click on the submit button. What I noticed is that the comment is added succesfully, but the state of the AddComment component for the comment isn't changed. When I click on submit button the input is submitted, but the function that changes the state is never called. I think the problem might be the fact that the actions don't synchronize.
I tried to use await act to render CommentsList component to make sure that the test run closer to how React works in the browser, but my AddComment component still doesn't disappear.
Here's my CommentsList component
function CommentsList(props) {
const { t } = useTranslation();
const [hasReplyCommentBox, setHasReplyCommentBox] = useState([]);
function toggleHasReplyComment(commentIndex) {
var auxState = { ...hasReplyCommentBox };
auxState[commentIndex] = auxState[commentIndex] ? 0 : 1;
setHasReplyCommentBox(auxState);
}
function replyToCommentButton(commentIndex) {
return [
<span
id={"replyButton-" + commentIndex}
onClick={() => toggleHasReplyComment(commentIndex)}>
{t('Reply to')}
</span>
];
}
function commentReplyBox(commentIndex, parentCommentId) {
return hasReplyCommentBox[commentIndex]
?
<AddComment
id={props.codeId}
parentCommentId={parentCommentId}
commentIndex={commentIndex}
toggleHasReplyComment={toggleHasReplyComment}
updateComments={props.updateComments}
/>
:
null
}
return (
<Layout>
<Layout>
<List
itemLayout="horizontal"
dataSource={props.comments}
renderItem={(comment, commentIndex) => (
<List.Item>
<CommentCard
userId={comment.user_id}
datePosted={comment.date_posted}
body={comment.body}
actions={replyToCommentButton(commentIndex)}
children={commentReplyBox(commentIndex, comment.id)}
/>
</List.Item>
)}
/>
<AddComment
id={props.codeId}
updateComments={props.updateComments}
/>
</Layout>
</Layout>
);
}
Here's my AddComment component
function AddComment(props) {
const { t } = useTranslation();
const { TextArea } = Input;
const [form] = Form.useForm();
const [comment, setComment] = useState();
const [onCommentAddSuccess, setOnCommentAddSuccess] = useState(0);
const buttonStyle = {
float: 'right'
};
function onCommentChange(newComment) {
setComment(newComment.target.value);
}
function updateOnCommentAddSuccess(onCommentAddSuccess) {
setOnCommentAddSuccess(onCommentAddSuccess + 1);
}
function resetCommentInput() {
setComment('');
}
function onFormReset() {
form.resetFields();
}
function toggleHasReplyCommentOnPost(parentCommentId, commentIndex) {
if (parentCommentId !== undefined) {
console.log('comentariu adaugat cu succes');
props.toggleHasReplyComment(commentIndex);
}
}
function submitComment() {
let request = {
body: comment,
code_id: props.id,
parent_comment_id: props.parentCommentId
};
fetch('/api/comment/add',
{
method: 'POST',
body: JSON.stringify(request)
}
).then(response => response.json())
.then(data => {
if (data.success === 1) {
updateOnCommentAddSuccess(onCommentAddSuccess);
props.updateComments(onCommentAddSuccess);
resetCommentInput();
toggleHasReplyCommentOnPost(props.parentCommentId, props.commentIndex);
}
});
}
return (
<>
<Form form={form} name="comment" className="comment-form"
onFinish={submitComment}>
<Form.Item name="body" label={t('Comment')}>
<TextArea placeholder={t('Leave a comment')}
onChange={onCommentChange}
id={"parent-comment-" + props.parentCommentId} />
</Form.Item>
<Form.Item style={buttonStyle}>
<Space>
{props.parentCommentId
?
<Button id={"cancelAddReplyComment-" + props.parentCommentId}
type="secondary" className = "comment-form-button"
onClick={
() => props.toggleHasReplyComment(props.commentIndex)
}>
{t('Cancel')}
</Button>
:
null
}
<Button type="primary" htmlType="submit"
className = "comment-form-button"
id={"post-comment-button-" + props.parentCommentId}
onClick={onFormReset}>
{t('Post')}
</Button>
</Space>
</Form.Item>
</Form>
</>
);
}
And here's how my test looks like
test ('Toggle displaying add reply to comments', async () => {
const comments = [
{
id: 'ID-1',
user_id: 'USER-ID-1',
date_posted: '2020-01-01 01:00:00',
body: 'First comment'
}
];
await act(async () => {
Promise.resolve(render(
<CommentsList comments={comments} />, container
));
});
// Open AddComment component
const replyButton = container.querySelector("#replyButton-0");
await fireEvent.click(replyButton);
// Insert input in the text area
const userInput = container.querySelector("#parent-comment-ID-1");
await userEvent.type((userInput), 'reply');
// Submit the input
const postButton = container.querySelector("#post-comment-button-ID-1");
await fireEvent.click(postButton);
// Check if the AddComment component is closed
expect(container.querySelector("#cancelAddReplyComment-ID-1")).toBeFalsy();
});

React - how to pass data into Modal (bootstrap)

I'm trying to pass data into Modal (bootstrap) popup and display some data.
I have a list of orders with a button 'display info', and every button that i press should display on the popup (Modal) diffrent data.
My question is how should i pass the data to the Modal?
this line <Button variant="primary" onClick={() => {this.handleModal(index)}}> Items info</Button> should trigger the Modal. In the handleModal function it passes the order index. And then i update the index on the setState of the handleModal function.
The Modal open but nothing passes to it.
I'm not sure that this is the correct way of doing it.
Also the Modal is inside the loop of the filteredOrders, should i move the Modal outside the loop?
And if yes, how should i do that and where?
import React, {useState} from 'react';
import './App.scss';
import {createApiClient, Item, Order} from './api';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import 'bootstrap/dist/css/bootstrap.min.css'
export type AppState = {
orders?: Order[],
search: string;
show:boolean;
item?: Item,
order_id: number,
}
const api = createApiClient();
export class App extends React.PureComponent<{}, AppState> {
state: AppState = {
search: '',
show:false,
order_id: 0,
};
searchDebounce: any = null;
async componentDidMount() {
this.setState({
orders: await api.getOrders()
});
}
async getItem(itemID: string){
this.setState({
item: await api.getItem(itemID)
});
}
render() {
const {orders} = this.state;
return (
<main>
<h1>Orders</h1>
<header>
<input type="search" placeholder="Search" onChange={(e) => this.onSearch(e.target.value)}/>
</header>
{orders ? <div className='results'>Showing {orders.length} results</div> : null}
{orders ? this.renderOrders(orders) : <h2>Loading...</h2>}
</main>
)
}
handleModal(index: number)
{
this.setState({
show:true,
order_id: index,
})
}
handleClose () {
this.setState({show: false})
}
renderOrders = (orders: Order[]) => {
const filteredOrders = orders
.filter((order) => (order.customer.name.toLowerCase() + order.id).includes(this.state.search.toLowerCase()));
const requiredItem = this.state.order_id;
const modelData = filteredOrders[requiredItem];
return (
<div className='orders'>
{filteredOrders.map((order,index) => (
<div className={'orderCard'}>
<div className={'generalData'}>
<h6>{order.id}</h6>
<h4>{order.customer.name}</h4>
<h5>Order Placed: {new Date(order.createdDate).toLocaleDateString()}</h5>
</div>
<div className={'fulfillmentData'}>
<h4>{order.itemQuantity} Items</h4>
<img src={App.getAssetByStatus(order.fulfillmentStatus)}/>
{order.fulfillmentStatus !== 'canceled' &&
<a href="#" onClick={() => this.ChangeStatus(order)}>Mark
as {order.fulfillmentStatus === 'fulfilled' ? 'Not Delivered' : 'Delivered'}</a>
}
</div>
<div className={'extraData'}>
<Button variant="primary" onClick={() => {this.handleModal(index)}}> Items info</Button>
<Modal show={this.state.show} >
{/*{console.log(modelData)}*/}
{/*<Modal.Header closeButton>*/}
{/* <Modal.Title>Item Info</Modal.Title>*/}
{/*</Modal.Header>*/}
<Modal.Body>
{ console.log(modaelData) }
</Modal.Body>
<Modal.Footer>
<Button onClick={() =>{ this.handleClose()}}>
Close
</Button>
</Modal.Footer>
</Modal>
</div>
<div className={'paymentData'}>
<h4>{order.price.formattedTotalPrice}</h4>
<img src={App.getAssetByStatus(order.billingInfo.status)}/>
</div>
</div>
))}
</div>
)
};
}
export default App;
I don't think you need to pass data to the Modal, but rather compose the Modal with the data in the first place. It is currently empty. Then you can continue to hide/show the complete Modal with handleModal.

onClick in button element not triggering re-render in react-table when using react hooks with graphql

I'm trying to render a table using react-table, however, this table has different states that are being pulled from a GraphQL database. Each button should effectively render the same UI for the table, but only display the shipments that have the correct status associated with what button the user clicked.
My shipments query is as follows:
import { gql } from 'apollo-boost';
export const GET_SHIPMENTS = gql`
{
shipments {
created_at
id
status
orders {
order_items
}
}
}
`;
My table component using the GET_SHIPMENTS query looks like this:
import React, { useState } from "react";
import { graphql } from 'react-apollo';
import { GET_SHIPMENTS } from '../graphql/ShipmentQueries';
import ReactTable from 'react-table';
import {
Card,
CardBody,
Row,
ButtonGroup,
Button
} from "reactstrap";
function OrderTable ({ loading, shipments }) {
const [shownShipment, setShownShipment] = useState({status: "created"});
const columns = [
{
Header: 'ID',
accessor: 'id',
},
{
Header: 'Status',
accessor: 'status',
},
{
Header: 'Item Count',
accessor: 'orders[0].order_items'
},
{
Header: 'Time Stamp',
accessor: 'created_at',
},
];
if (loading) return <p>Loading...</p>;
return (
<div className="content">
<ButtonGroup className="center">
<Button
name="created"
onClick={() => setShownShipment(shownShipment.status === "created")}
>
Created
</Button>
<Button
name="awaiting driver"
onClick={() => setShownShipment(shownShipment.status === "awaiting_driver")}
>
Awaiting Driver
</Button>
<Button
name="delivered"
onClick={() => setShownShipment(shownShipment.status === "delivered")}
>
Delivered
</Button>
</ButtonGroup>
<Row className="mt-5">
<Card>
<CardBody>
<ReactTable
data={shipments}
columns={columns}
sortable={true}
resizable={false}
minRows={10}
/>
</CardBody>
</Card>
</Row>
</div>
);
}
export const OrderTableWithData = graphql(GET_SHIPMENTS, {
props: ({data: { loading, shipments, shownShipments }}) => ({
loading,
shipments,
shownShipments,
}),
})(OrderTable);
This is my first introduction into using hooks, so I know that I'm probably not utilizing them properly. I'm not sure if I have to use the useEffect hook or not. I've scoured the Hooks docs and can't seem to find a clear answer. I feel like useState should work. Do I have to re-render the entire ReactTable element?
If you want shownShipment.status to be the string corresponding to the delivery status (such as 'delivered', 'awaiting_driver') etc, your buttonClick code should look like this:
onClick={() => setShownShipment({status: "delivered"}) }
Try adding an effect to the react component like this to see the status updating after you click each button:
useEffect(() => { console.log(shownShipment); }, [shownShipment]);
Now that you have shownShipment set to the desired status, you can filter your total list of shipments from GQL based on this. UseState again for the list of shipments you will actually give to your table. useEffect will be helpful here as well. Something like this:
// at the top of the component
var [shipmentsToDisplay, setShipmentsToDisplay] = useState([]);
useEffect(() => {
// loop through all shipments and create a new array for only the ones
// you want to show.
const filteredShipments =
shipments &&
shipments.map(shipment => {
if (shipment.status === shownShipment) {
return shipment;
}
}
setShipmentsToDisplay(filteredShipments);
}, [shownShipment, shipments]);
// Render your table with these shipments rather than props.shipments
<ReactTable
data={shipmentsToDisplay}
columns={columns}
sortable={true}
resizable={false}
minRows={10}
/>
The solution for this code was as follows:
(Some code removed for brevity)
function OrderTable ({ loading, shipments }) {
const [shipmentsToDisplay, setShipmentsToDisplay] = useState([]);
useEffect(() => {
if(shipments) {
filterShipments("created");
}
}, [shipments]);
function filterShipments(status) {
let shownShipments = [];
shipments.forEach(element => {
if(element.status === status) {
shownShipments.push(element);
}
});
setShipmentsToDisplay(shownShipments);
}
if (loading) return <Loading />;
return (
<div className="content">
<ButtonGroup className="center">
<Button name="pending" onClick={() => filterShipments("created")}>
Pending
</Button>
<Button name="ready" onClick={() => filterShipments("awaiting_driver")}>
Ready
</Button>
<Button name="completed" onClick={() => filterShipments("delivered")}>
Completed
</Button>
</ButtonGroup>
<Row className="mt-5">
<ReactTable
data={shipmentsToDisplay}
columns={columns}
sortable={true}
resizable={false}
defaultPageSize={5}
className="-striped -highlight"
getTrProps={getTrProps}
SubComponent={row => {
return (
<ShipmentDetails
shipments={shipments}
/>
)
}}
/>
</Row>
</div>
);
}
export const OrderTableWithData = graphql(GET_SHIPMENTS, {
props: ({data: { loading, shipments }}) => ({
loading,
shipments
}),
})(OrderTable);
The solution was to land on the Pending shipments, so for my state, I used the useEffect hook to land on the shipments that have created as a status.
For my onClick function, I decided to filter through each shipment starting with the created shipments and update the state based on which status is associated with the button click. I don't know if that makes sense or not. I'm new to hooks and GraphQL.

Pass item data to a react modal

I have a map that render few items and one of its line is below
<a onClick={()=> this.setState({"openDeleteModal":true)}>Delete</a>
Obviously I want to open a modal when user click the delete, but I have to pass a few things like the name of the item, id of the item to perform the deletion. How can I pass says the name to the modal?
I can bind the obj name to a like this
Delete
Am I on the right track?
When working on React applications, try not to think in terms of passing values to other components, but rather updating state that your components are exposed to.
In your example, assuming your modal component is a child of the same component your list of a tags belongs to, you could set the values you are interested in exposing to the modal on the state, as well as updating the property that signals whether the modal is open or not. For example:
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
openDeleteModal: false,
activeItemName: '', //state property to hold item name
activeItemId: null, //state property to hold item id
}
}
openModalWithItem(item) {
this.setState({
openDeleteModal: true,
activeItemName: item.name,
activeItemId: item.id
})
}
render() {
let buttonList = this.props.item.map( item => {
return (<button onClick={() => this.openModalWithItem(item)}>{item.name}</button>
});
return (
<div>
{/* Example Modal Component */}
<Modal isOpen={this.state.openDeleteModal}
itemId={this.state.activeItemId}
itemName={this.state.activeItemName}/>
{ buttonList }
</div>
)
}
}
Copying over my answer from How to pass props to a modal
Similar scenario
constructor(props) {
super(props)
this.state = {
isModalOpen: false,
modalProduct: undefined,
}
}
//****************************************************************************/
render() {
return (
<h4> Bag </h4>
{this.state.isModalOpen & (
<Modal
modalProduct={this.state.modalProduct}
closeModal={() => this.setState({ isModalOpen: false, modalProduct: undefined})
deleteProduct={ ... }
/>
)
{bag.products.map((product, index) => (
<div key={index}>
<div>{product.name}</div>
<div>£{product.price}</div>
<div>
<span> Quantity:{product.quantity} </span>
<button onClick={() => this.props.incrementQuantity(product, product.quantity += 1)}> + </button>
<button onClick={() => this.decrementQuantity(product)}> - </button> // <----
</div>
</div>
))}
)
}
//****************************************************************************/
decrementQuantity(product) {
if(product.quantity === 1) {
this.setState({ isModalOpen: true, modalProduct: product })
} else {
this.props.decrementQuantity(product)
}
}
Try this: this is the form which has the button, and is a child component of some other component that passes the handleButtonAction method as props, and the button takes the input data and invokes this parent component method
handleSubmit = (e) => {
e.preventDefault();
const data = e.target.elements.option.value.trim();
if (!data) {
this.setState(() => ({ error: 'Please type data' }));
} else {
this.props.handleButtonAction(data, date);
}
}
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit={this.handleSubmit}>
<input type="text" name="option"/>
<div>
<button>Get data</button>
</div>
</form>
The parent component:
handleButtonAction = (data) => {
axios.get(`http://localhost:3000/someGetMethod/${data}`).then(response => {
const resData = response.data;
this.setState({
openModal: true,
status: response.status,
data: resData
});
}).catch((error) => {
if (error.message.toLowerCase() === 'network error') {
this.setStateWithError(-1, {});
}
else { // not found aka 404
this.setStateWithError(error.response.status, '', {currency, date: ddat});
}
});
}

Categories