React - how to pass data into Modal (bootstrap) - javascript

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.

Related

In react, how to pass states down as props?

This is a very noob question but I've been trying all day do implement this. Please help me out.
Sorry for the length, just tried to put out the whole thing I am struggling with
I am trying to build custom buttons and to do so, I created a component so I can create as many buttons that I want. For that I declared a state and passed down some information as props, which is as follows:
import React, {useState} from 'react'
import Button from '../components/Button'
function CustomButton() {
const [clicked, setClicked] = useState(false)
return (
<div className='CustomButton'>
<Navbar />
<Button setClicked={setClicked} name="Button One" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Two" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Three" clicked={clicked}/>
</div>
)
}
export default CustomButton
As you can see, we passed the state and name of that button down. To render this Buttons, following component has been created:
import React from 'react'
import Modal from './Modal/Modal'
function Button({setClicked, name, clicked}) {
return (
<div>
<button onClick={() => {setClicked(true)}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
export default Button
And lastly, when once a button is clicked, we want to perform some action. That action is to pop the Modal on a screen. And to do so, we created a Modal and passed down few props. Code for the same is as follows:
import React from 'react'
function Modal({closeModal, name}) {
return (
<div className='modal'>
<div className='modalContainer'>
<p>{name}</p>
<div>
<button onClick={() => {closeModal(false)}}>×</button>
</div>
</div>
</div>
)
}
export default Modal
The expected result is for a Modal to pop with "You clicked button One", supposing we clicked one something similar to this.
The actual result is that all three Modals pop up one above the other when any of the three buttons are passed. The result:
I realize that I am passing the states wrong way. When any of the button is clicked all three get set to true. I simply don't realize how. Don't they create a method for each one?
Also, can you guys please teach me a better/understandable way to write clicked logic. Like maybe
if(clicked){
<Modal closeModal={setClicked} name={`You Clicked ${name}`} />
}
Because you bind all three buttons with one state, You need a state as array, with items equal to the number of buttons.
const [clicked, setClicked] = useState([false, false, false])
return (
<div className='CustomButton'>
<Navbar />
{
clicked.map((button, i) => {
return <Button setClicked={setClicked} name="Button Three" clicked={clicked[i]} index={i}/>
})
}
</div>
)
Then in the button component.
function Button({setClicked, name, clicked, index}) {
return (
<div>
<button onClick={() => {setClicked(prev => prev.map((item, i) => {
return i === index ? true : item
}))}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
And the modal component.
function Modal({ closeModal, name, index }) {
return (
<div className="modal">
<div className="modalContainer">
<p>{name}</p>
<div>
<button
onClick={() => {
closeModal((prev) =>
prev.map((item, i) => {
return i === index ? false : item;
})
);
}}
>
×
</button>
</div>
</div>
</div>
);
}
You can find a working example on this link.
https://codesandbox.io/s/old-wood-zgjno9
You can implement multiple modals like this:
import { useState } from "react";
export default function App() {
const [showModal1, setShowModal1] = useState(false);
const [showModal2, setShowModal2] = useState(false);
return (
<div className="App">
<button onClick={(e) => setShowModal1(true)}>Button 1</button>
<button onClick={(e) => setShowModal2(true)}>Button 2</button>
{showModal1 && (
<Modal text="Modal 1" onClose={(e) => setShowModal1(false)} />
)}
{showModal2 && (
<Modal text="Modal 2" onClose={(e) => setShowModal2(false)} />
)}
</div>
);
}
const Modal = ({ text, onClose }) => {
return (
<div>
{text}
<button onClick={onClose}>Close</button>
</div>
);
};
Working example

Callback to control modal display

I have built a modal to display login/register modal. By default, the modal is opened by another component using the props show. This working when the modal is called by this component.
Also the modal Form is called from my Header.js as shown below:
<LoginRegisterForm displayPopUp={this.state.showLogin} onHide={() => this.setState({ showLogin: false })}/>}
In this case, the state showLogin is set to true when clicking on the Login/Register, the <LoginRegisterform is now showing the modal because displayPopup props is set to true
The code is below:
Form.js
const Form = ({ initialState = STATE_SIGN_UP, displayPopUp}) => {
const [mode, toggleMode] = useToggle(initialState);
const [display, toggleDisplay] = useToggleDisplay(displayPopUp);
console.log('----------------------------------------------------------')
console.log('displayPopUp: ' + displayPopUp)
console.log('display: ' + display)
console.log('toggleDisplay: ' + toggleDisplay)
console.log('----------------------------------------------------------')
return (
<Modal className="modal" show={displayPopUp} size="lg">
<Container pose={mode === STATE_LOG_IN ? "signup" : "login"}>
<div className="container__form container__form--one">
<FormLogin mode={mode} toggleDisplay={toggleDisplay} />
</div>
<div className="container__form container__form--two">
<FormSignup mode={mode} toggleDisplay={toggleDisplay}/>
</div>
<Overlay toggleMode={toggleMode} mode={mode} />
</Container>
</Modal>
);
};
in the FormLogin, I do have a Cancel button which allow me to close the modal located in the Form.js when needed. However, I do not know how I can make the modal close by change the show params in the Form.js when the close control is in the class FormLogin
FormLogin.js
import React from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import SocialButton from './styled/SocialButton'
import SlidingForm from './styled/SlidingForm'
import WhiteButton from '../../materialdesign/WhiteButton'
import { faFacebook, faGoogle, faLinkedinIn } from '#fortawesome/free-brands-svg-icons'
import Auth from '../../../data/network/Auth';
import Constant from '../../../config/Constant';
import CancelIcon from '#material-ui/icons/Cancel';
class FormLogin extends React.Component {
constructor(props, context) {
super(props);
this.state = {
email: '',
password: '',
loading: false,
error: '',
toggleDisplay: this.props.toggleDisplay
};
}
requestSignIn = async (event) => {
event.preventDefault();
this.setState({loading: true})
try {
const authData = await Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem(Constant.ALL, authData)
sessionStorage.setItem(Constant.AUTH_TOKEN, authData.token)
sessionStorage.setItem(Constant.DISPLAY_NAME, authData.user_display_name)
sessionStorage.setItem(Constant.EMAIL, authData.user_email)
sessionStorage.setItem(Constant.NICENAME, authData.user_nicename)
window.open("/", "_self") //to open new page
this.setState({loading: false })
this.close()
} catch (error) {
console.warn("Connection to WP - Auth Token failed ")
console.error(error);
}
}
requestForgotPassword = () => {
}
handleOnChange = (event) => {
this.setState({[event.target.name]: event.target.value})
}
render(){
const { email, password } = this.state;
return(
<SlidingForm>
<div style={{textAlign:"left"}}>
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.state.toggleDisplay(false) }/>
</div>
<h1 style={titleStyle}>Sign in</h1>
<div style={{textAlign: "center"}}>
<SocialButton>
<FontAwesomeIcon icon={faFacebook} />
</SocialButton>
<SocialButton>
<FontAwesomeIcon icon={faGoogle} />
</SocialButton>
<SocialButton>
<FontAwesomeIcon icon={faLinkedinIn} />
</SocialButton>
</div>
<p style={txtStyle}>or use your account</p>
<form style={{textAlign: "center"}}>
<input style={formStyle} placeholder="Email" type="text" name="email" value={ email } onChange={ this.handleOnChange }/>
<input style={formStyle} placeholder="Password" type="password" name="password" value={ password } onChange={ this.handleOnChange } />
</form>
<p style={txtSpan}>
<a href="#" onClick={this.requestForgotPassword}>Forgot your password?</a>
</p>
<div style={{textAlign: "center", marginTop: "15px"}}>
<WhiteButton text="Sign in" onClick={this.requestSignIn}></WhiteButton>
</div>
</SlidingForm>
);
}
}
export default FormLogin
For now I was doing this :
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.state.toggleDisplay(false)
but it's not working, it's seems not having control on the Form.js.
toggleDisplay code is below:
import { useState } from 'react'
export const STATE_SHOW = true
export const STATE_HIDE = false
const useToggleDisplay = initialDisplayState => {
const [display, setDisplay] = useState(initialDisplayState)
const toggleDisplay = () =>
setDisplay(display === false ? true : false)
console.log('-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
console.log('display: ' + display)
console.log('toggleDisplay: ' + toggleDisplay)
console.log('-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
return [display, toggleDisplay]
}
export default useToggleDisplay
The Overall logic:
Modal is called from Header.js and show is set to false by default and switch to true when clicking on the menu option login
The Modal Form.js is handling login and register screen
What is the best option to be able to set show to false in the Form.js when the close is triggered in the FormLogin.js ?
Thanks
Instead of assigning the toggleDisplay prop to local state, just invoke the prop directly. This should work for updating the <Form />'s display state to false.
Also, do you intend for the <CancelIcon /> to toggle the modal open/close state, or is it just to close the modal? If it's the latter, you may want to update the prop name to closeModal instead of toggleDisplay.
<div style={{textAlign:"left"}}>
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.props.toggleDisplay(false)
}/>
</div>
Your useToggleDisplay func is confusing, and the original version was not accepting any arguments for toggleDisplay, hence even though you passed in false, it did not update the display state. I've removed useToggleDisplay since it doesn't do anything special.
const [display, setDisplay] = useState(initialDisplayState)
I also realised that <Modal /> is accepting displayPopUp instead of display. If you use displayPopUp, it doesn't know that display has been set to false, and therefore, remain open. And I passed setDisplay setter to the <FormLogin /> components.
<Modal className="modal" show={display} size="lg">
<Container pose={mode === STATE_LOG_IN ? "signup" : "login"}>
<div className="container__form container__form--one">
<FormLogin mode={mode} toggleDisplay={setDisplay} />
</div>
<div className="container__form container__form--two">
<FormSignup mode={mode} toggleDisplay={setDisplay}/>
</div>
<Overlay toggleMode={toggleMode} mode={mode} />
</Container>
</Modal>

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

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

React Modals: Modal component only opens the first-clicked modal and none else

I am new to StackOverflow so pardon me for bad wording of the problem.
I am learning React currently and as a small project, I am creating a course-search app which will filter the courses on the basis of input entered by the user from a JSON file and display them.
I have added modals to each card component which should open on the click of a button within the card.
The problem I'm facing is that when I click the button, it only opens the modal first clicked one, and none else.
Here is the code:
import MyModal from '../Modal/Modal'
import courseList from "./courses.json";
class App extends Component {
state = {
search: "",
modal: false,
};
selectModal = (id) => {
this.setState({
modal: {
[id]: !this.state.modal
}
})
}
rendercourse = course => {
var dep = course.dep;
return (
<div className="col-md-3" style={{ marginTop: "20px" }}>
<Card className="card">
<CardBody className={dep}>
<CardTitle title={course.id}>
{course.code}
</CardTitle>
<CardText className="title">{course.name}</CardText>
<p className="ic">{course.ic}</p>
Units: {course.unit}<br />
<button onClick= {
this.selectModal.bind(this, course.code)}>More Info</button>
</CardBody>
<MyModal
displayModal={this.state.modal[course.code]}
coursename={course.name}
coursecode={course.code}
courseic={course.ic}
coursedep={course.dep}
courseunit={course.unit}
closeModal={this.selectModal} />
</Card>
</div>
);
};
onchange = e => {
this.setState({ search: e.target.value });
};
render() {
const { search } = this.state;
const filteredcourses = courseList.filter(course => {
return course.name.toLowerCase().indexOf(search.toLowerCase()) !== -1;
});
return (
<div className="flyout">
<main style={{ marginTop: "4rem" }}>
<div className="container">
<div className="row">
<div className="col-12">
<center>
<h3>
Search for a course
</h3>
</center>
</div>
<div className="col">
<Input
label="Enter the name of the course"
icon="search" className="in"
onChange={this.onchange}
/>
</div>
<div className="col" />
</div>
<div className="row">
{filteredcourses.map(course => {
return this.rendercourse(course);
})}
</div>
</div>
</main>
</div>
);
}
}
export default App;
And here is the modal component:
const MyModal = props => {
function displayInfo () {
return (
<div>
<div>{props.coursename}</div>
<div>{props.courseic}</div>
<div>{props.courseunit}</div>
<div>{props.coursedep}</div>
<div>{props.coursecode}</div>
</div>
);
}
function closeModal (e) {
e.stopPropagation()
props.closeModal()
}
let modal = (
<div
className="modal"
onClick={ closeModal }>
<div className="modal-content"
onClick={ e => e.stopPropagation() }>
<span
className="close"
onClick={ closeModal }>×
</span>
<div className="modal-flex">
{displayInfo()}
</div>
</div>
</div>
)
return ( props.displayModal ? modal : null);
}
export default MyModal;
I want the card-specific modal to open up whenever the button is clicked.
Your selectModal function doesn't flip the state of each modal, but only that of the first one.
If any modal is defined in the state (be it the first or any other modal), this.state.modal will evaluate to true. (an object with at least some content is true)
To allow all your modals to be opened at the same time simply flip the value of the modal element in your modal state object.
this.state.modal[id] is undefined by default, which will evaluate to boolean false.
selectModal = (id) => {
this.setState({
modal: {
[id]: !this.state.modal[id]
}
})
}
If you'd rather only open one modal at a time, you should simply store the id of your modal in the state instead of tracking all modals in there:
selectModal = (id) => {
this.setState({
modal: id
})
}
To close a modal change your closeModal prop to pass undefined/false instead of the modal ID and change your modal display prop so it will perform the check whether your modal state and the coursecode match:
this.state.modal === course.code

React - Mapped array not passing props correctly to child component

I am making a dashboard component which displays rendered previews and code for HTML snippets. Inside of the dashboard component I am mapping the array of snippets using .map. Each mapped snippet is going to have a delete function (already built) and an update function.
For the update function to work each snippet has it's own child modal component. I need to pass the ID of the snippet to the modal component where I can combine the ID with the new content before updating the database and state.
However, I'm making a mistake somewhere as I pass the ID as props to the modal.
.map used inside of my Dashboard.js Dashboard class component.
{this.state.snippets.map(snippet => (
<>
<div key={snippet._id} className="holder--pod">
<div className="content">
<div className="content__snippet-preview">
Snippet preview
</div>
<div className="content__body">
<h4>{snippet.name}</h4>
<p>{snippet.details}</p>
<p>{snippet._id}</p> //THIS WORKS
<pre>
<code>{snippet.content}</code>
</pre>
</div>
<div className="content__button">
<button onClick={this.handleDelete(snippet._id)}>
Delete
</button>
<button type="button" onClick={this.showModal}>
Open
</button>
</div>
</div>
</div>
<Modal
sid={snippet._id} //PASS ID HERE
show={this.state.show}
handleClose={this.hideModal}
></Modal>
</>
))}
This renders the snippets below (3 snippet pods, with their database ID included).
The open button opens the modal (Modal.js) below.
import React, { Component } from 'react'
import api from '../api'
export default class Modal extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
details: '',
content: '',
message: null,
}
}
handleInputChange = event => {
this.setState({
[event.target.name]: event.target.value,
})
}
handleClick = id => event => {
event.preventDefault()
console.log(id)
}
render() {
const { sid, show, handleClose } = this.props
console.log(sid)
const showHideClassName = show ? 'modal display-flex' : 'modal display-none'
return (
<div id="Modal" className={showHideClassName}>
<div id="modal-main">
<h4>Edit snippet {sid}</h4>
<form>
Name:{' '}
<input
type="text"
value={this.state.name}
name="name"
onChange={this.handleInputChange}
/>{' '}
<br />
Details:{' '}
<input
type="text"
value={this.state.details}
name="details"
onChange={this.handleInputChange}
/>{' '}
<br />
Content:{' '}
<textarea
value={this.state.content}
name="content"
cols="30"
rows="10"
onChange={this.handleInputChange}
/>{' '}
<br />
<button onClick={this.handleClick(sid)}>TEST ME</button>
</form>
<button onClick={handleClose}>Close</button>
{this.state.message && (
<div className="info">{this.state.message}</div>
)}
</div>
</div>
)
}
}
The console.log just under the render actually pastes the correct 3 ID's the console.
However, calling the ID (sid) within the Modal.js return will only show the last snippet ID, no matter which Modal I open. The same goes for pushing that ID to the handleClick function where I intend to combine the ID with an update package.
Solution below as initiated by HMR in the comments.
The problem was all the modals were showing and just the last one was visible.
Fixed by moving the modal out of the .map and instead updating the ID from within the .map to the state and passing the state ID to a new nested component within the modal.
Also switched to using dynamic CSS to show and hide the modal based on the state.
Dashboard.jsx
export default class Snippets extends Component {
constructor(props) {
super(props)
this.showModal = React.createRef()
this.state = {
snippets: [],
show: false,
sid: '',
}
}
handleDelete = id => event => {
event.preventDefault()
api
.deleteSnippet(id)
.then(result => {
console.log('DATA DELETED')
api.getSnippets().then(result => {
this.setState({ snippets: result })
console.log('CLIENT UPDATED')
})
})
.catch(err => this.setState({ message: err.toString() }))
}
handleModal = id => {
this.setState({ sid: id })
this.showModal.current.showModal()
}
//<div id="preview">{ReactHtmlParser(snippet.content)}</div>
render() {
return (
<>
<Modal ref={this.showModal} handleClose={this.hideModal}>
<ModalUpdate sid={this.state.sid} />
</Modal>
<div className="Dashboard">
<div className="wrapper">
<div className="container">
<div className="holder">
<div className="content">
<div className="content__body">
<h3>Dashboard</h3>
</div>
</div>
</div>
<div className="break"></div>
{this.state.snippets.map(snippet => (
<div key={snippet._id} className="holder--pod">
<div className="content">
<div className="content__snippet-preview">
Snippet preview
</div>
<div className="content__body">
<h4>{snippet.name}</h4>
<p>{snippet.details}</p>
<p>{snippet._id}</p>
<pre>
<code>{snippet.content}</code>
</pre>
</div>
<div className="content__button">
<button onClick={this.handleDelete(snippet._id)}>
Delete
</button>
<button
type="button"
onClick={() => this.handleModal(snippet._id)}
>
Open
</button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</>
)
}
Modal.jsx
import React, { Component } from 'react'
export default class Modal extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
}
}
showModal = () => {
this.setState({ show: true })
}
hideModal = () => {
this.setState({ show: false })
}
render() {
return (
<div
id="Modal"
style={{ display: this.state.show === true ? 'flex' : 'none' }}
>
<div id="modal-main">
<h4>Edit snippet </h4>
{this.props.children}
<button onClick={() => this.hideModal()}>Close</button>
</div>
</div>
)
}
}
ModalUpdate.jsx
import React, { Component } from 'react'
export default class ModalUpdate extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
details: '',
content: '',
message: null,
}
}
// handleInputChange = event => {
// this.setState({
// [event.target.name]: event.target.value,
// })
// }
// handleClick = id => event => {
// event.preventDefault()
// console.log(id)
// }
render() {
return <h4>ID = {this.props.sid}</h4>
}
}
I am not sure about the handleDelete function,. but replacing the line should solve the issue probably
<button onClick={() => this.handleDelete(snippet._id)}>
One potential issue is the this.handleDelete(snippet._id) will fire immediately rather than onClick, so you will need to add an anonymous function in the event listener:
() => this.handleDelete(snippet._id)
instead of
this.handleDelete(snippet._id)

Categories