i’m new to ReactJS. And I’m trying to create an app which displays a modal with different parameters. My problem is, I have a reusable modal component, but how do I achieve when pressing/clicking a button inside the modal will re-render the whole modal component with different results/value?
Here is my code:
Modal.js
import { useEffect, useRef, Fragment, Link, useState } from "react";
import { useParams, useNavigate, useLocation } from "react-router-dom";
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
import airportData from '../airports.json';
export function Modal() {
const modalRef = useRef();
const { id } = useParams();
const navigate = useNavigate();
const [reload, setReload] = useState(false);
useEffect(() => {
const observerRefValue = modalRef.current;
disableBodyScroll(observerRefValue);
return () => {
if (observerRefValue) {
enableBodyScroll(observerRefValue);
}
};
}, []);
function setImageUrlLarge(url) {
return {
backgroundImage: `url(${process.env.PUBLIC_URL}/assets/images/large/${url}.jpg)`
}
}
return (
<div ref={modalRef} className="modal-wrapper">
<div className="modal">
<div className={`detail ${id} overlay`} style={setImageUrlLarge(id)}>
<a className='overlay' rel='noopener'></a>
<AirportDetails id={id} airportData={airportData} />
</div>
</div>
</div>
)
function AirportDetails({id, airportData}) {
const [data, setData] = useState(airportData);
const [airportId, setAirportId] = useState(id);
const airport = airportData.filter((airport) => airport.id.toLowerCase().includes(id.toLowerCase()))[0];
const [description, setDescription] = useState(airport.description);
const pattern = /*([A-Za-z])*/gi;
const em = description.replace(pattern, '<em>$1</em>');
const currUrl = window.location.href;
function capitalize(str) {
return str.toUpperCase();
}
const share = (socialType) => e => {
if (socialType == "twitter") {
const text = `Making sense of those three-letter airport codes: ${capitalize(id)}`;
const link = `https://twitter.com/intent/tweet?url=${currUrl}&text=${text}`;
return link;
}
const link = `https://www.facebook.com/dialog/share?display=popup&href=${currUrl}${id}&redirect_uri=${currUrl}${id}`;
return link;
}
function setTo(social) {
if(social=="twitter") {
return "https://twitter.com/intent/tweet?url=$SHARE_URL&text=$TEXT";
}
else {
return "https://www.facebook.com/sharer/sharer.php?u=$SHARE_URL";
}
}
return (
<div className='container'>
<div className='detail-info'>
<h1>{airport.id}</h1>
<h2>{airport.name}</h2>
<h3><span className="local_name">{airport.local_name}</span></h3>
<h4>{airport.city}</h4>
<div className="description fl-edu">
<p dangerouslySetInnerHTML={{ __html: em}}></p>
</div>
<a className="close-detail" role="button" onClick={() => navigate('/')}></a>
<a className="random" role="button" onClick={() => {randomAirport()}}>
Random Airport</a>
<div className="social">
<a role="button" className="twitter" href={setTo("twitter")} onClick={() => {share("twitter")}} target="_blank"></a>
</div>
<div className="social">
<a className="facebook" href={setTo("facebook")} onClick={() => {share("facebook")}} target='_blank'></a>
</div>
</div>
<div className="photo-credit">
Photo by <a>{airport.imageCredit}</a>
</div>
<a className="back" role="button" onClick={() => navigate('/')}>Airport Codes PH</a>
</div>
)
}
function randomAirport() {
const rand = Math.floor(Math.random() * airportData.length);
const airportRandData = airportData[rand];
setReload(!reload);
console.log(reload);
}
}
What i’m achieving right now is to change the state of the modal every time when the button is clicked and randomizing the value of an object. I’m expecting to reload the modal component with a different value from the randomAirport function and passing the airportData back to the Modal component.
2
Answers
The idea should be not to rerender the modal with different content, but to simply keep it open and make the component it renders re-render with it’s own new state/content/etc.
Refactor your code to:
Separate & declare
AirportDetails
outside theModal
component so it’s not redeclared each render cycle. This fixes a React anti-pattern. Remove thereload
state, it’s also an anti-pattern.Update
AirportDetails
to initialize thedata
state to a random airport’s data, and update theonClick
handler to to select a random airport’s data and enqueue a state update. Render all the airport details from the localdata
state.To re-rendering the modal component, you need to update the current state in the Modal component and pass that state down to the desired component, in your project that is AirportDetails component, you have to create this component separately and pass data to the "AirportDetails" by props.
In your parent component, that means where all these data are generating add we have get the unique id for the airport,
Then we have to update data props,
Also we need to update random airport function to set "current airport id"
ok, Now for the separate component called AirportDetails component, we need to get those sending values from parent.
To find airport data you can reuse your method,
Also to share your links u can use window.open
For reload u can add your random airport button