Is it possible to render and append a new component after click? I have my calendar component
return (
<span
key={date.toString()}
className={"day" + (isToday ? " today" : "") + (isCurrentMonth ? "" : " different-month") + (date.isSame(selected) ? " selected" : "")}
onClick={()=>select(day)}>{number}</span>
);
onClick I want to render my jsWindow Component
render(){
return (
<div className="js-window">
{console.log(this.props)}
<Icon icon={close} className="closeBtn" size={24} onClick={(e)=>e.target.parentNode.parentNode.remove()}/>
{this.props.children}
</div>
)
}
After click close icon I want remove JsWindow.
You can use conditional rendering:
{ condition && <Component /> }
Put the selected day in component state and render your component based on that. On click you set or reset the state, which will trigger a rerender.
Basic example to get you started:
state = {
day: undefined
}
selectDay = day => {
this.setState({day});
};
resetDay = () => {
this.setState({day: undefined});
};
render(){
const day = this.state.day;
return day ? (
<div className="js-window">
<Icon icon={close} className="closeBtn" size={24} onClick={this.resetDay}/>
{this.props.children}
</div>
) : (
<span key={date.toString()} className={} onClick={()=>this.selectDay(day)}>{number}</span>
)
)
}
Related
I have created a carousel which is a column of ten dates ,for this i am mapping dates by momentjs, Inside each of this column , i am mapping different time slots for morning ,afternoon and evening,
and i have a functionality that only shows first two time slots and then there is a show more button, by clicking on this button more time slots are appear,but whenver i am clicking on this button all of the columns time slots is appearing, i have to handle all the column button individually..
Thank You in adavance... :)
below is my code...
const [showMoreClicked, setShowMoreClicked] = useState(false);
const [showMoreAfternoon, setShowMoreAfternoon] = useState(false);
const [showMoreEvening, setShowMoreEvening] = useState(false);
const showMoreSlotsForMorning = (e) => {
e.preventDefault();
setMoreClicked(!showMoreClicked);
};
const showMoreSlotsForAfternoon = (e) => {
e.preventDefault();
setShowMoreAfternoon(!showMoreAfternoon);
};
const showMoreSlotsForEvening = (e) => {
e.preventDefault();
setShowMoreEvening(!showMoreEvening);
};
<Carousel responsive={responsive}>
{nexttendates.map((elem, dateIndex) => {
return (
<div>
<button key={dateIndex} className="nexttendates">
{elem}
</button>
<div className="appointment-timelots">
<div className="availableslots">
<div className="availableslot">
<img
src="../elements/doctorlist/doctorcard/sunrise.png"
alt=""
className="sunrise"
/>
Morning
</div>
</div>
</div>
{morningtime.map((elem, morInd, arr) => {
if (showMoreClicked == false) {
while (morInd == 0 || morInd == 1)
return (
<button key={morInd} className="appointtimes">
{elem}
</button>
);
} else {
return (
<button key={morInd} className="appointtimes">
{elem}
</button>
);
}
})}
<button
choseIndex={dateIndex}
onClick={showMoreSlotsForMorning}
className="appointtimes"
>
{showMoreClicked ? "Show Less" : "Show More"}
</button>
<img
src="../elements/doctorlist/doctorcard/sun.png"
alt=""
className="afternoon"
/>
Afternoon
{afternoontime.map((elem, aftInd) => {
if (showMoreAfternoon == false) {
while (aftInd == 0 || aftInd == 1)
return (
<button className="appointtimes">{elem}</button>
);
} else {
return (
<button className="appointtimes">{elem}</button>
);
}
})}
<button
choseIndex={dateIndex}
onClick={showMoreSlotsForAfternoon}
className="appointtimes"
>
{showMoreAfternoon ? "Show Less" : "Show More"}
</button>
<img
src="../elements/doctorlist/doctorcard/night-mode.png"
alt=""
className="evening"
/>
Evening
{eveningtime.map((elem, eveInd) => {
if (showMoreEvening == false) {
while (eveInd == 0 || eveInd == 1) {
return (
<button className="appointtimes">{elem}</button>
);
}
} else {
return (
<button className="appointtimes">{elem}</button>
);
}
})}
<button
choseIndex={dateIndex}
onClick={showMoreSlotsForEvening}
className="appointtimes"
>
{showMoreEvening ? "Show Less" : "Show More"}
</button>
</div>
);
})}
</Carousel>
i think its happening because of i have mapped an array and only used one useState to check open or not...Can anybody plz help me....
Make the time slots list as a separate component, so that each of the morning, afternoon, and evening list will have their own state automatically for toggling display.
Something like this example:
import { useState } from "react";
// Toggle showMore value on click
const SlotsList = ({ slots }) => {
const [showMore, setShowMore] = useState(false);
const handleShowMoreClick = () => {
setShowMore((prev) => !prev);
};
// Filter the slots prop before map it if showMore is false
return (
<div>
{slots
.filter((elem, index) => (showMore ? true : index <= 1))
.map((elem, index) => (
<button key={index} className="appointtimes">
{elem}
</button>
))}
<button onClick={handleShowMoreClick} className="appointtimes">
{showMore ? "Show Less" : "Show More"}
</button>
</div>
);
};
export default SlotsList;
In this example, the list is filtered before being mapped out for an easier solution. The key property should be replaced by a unique ID to avoid conflict.
It can then be imported and used like below in the main component. Also reusable for all 3 lists, and each have separate display toggle.
<SlotsList slots={morningtime} />
I'm trying to build a Flip card with ReactJS, that have inside 2 others components which are : Frontside and BackSide. These components should have children such as BackgroundCard or Sectioned Card. When I test the component I'm not getting anything on the screen and there is no errors in the console!
FlipContent.js
function FlipContent() {
const [setFront, setFrontState] = useState(true);
const [setBack, setBackState] = useState(false);
const [setFlipped, setFlippedState] = useState("");
function FlippingCard() {
setFrontState(setFront === true ? false : true);
setBackState(setBack === false ? true : false);
setFlippedState(setFlipped === "" ? "flipped" : "");
}
return (
<div className={`flip-content ${setFlipped}`} onClick={FlippingCard} >
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{setFront ? <FrontSide></FrontSide> : null}
{setBack ? <BackSide> </BackSide> : null}
</div>
</div>
);
}
And For the FrontSide/BackSide same this as this code
function FrontSide({ children }) {
return (
<div className="flip-content-front">
<div style={{ cursor: "pointer" }}>
{children}
</div>
</div>
);
}
and here how I'm trying to preview the component
function FlipPreview() {
return (
<Column>
<Row className={css(styles.title)} wrap flexGrow={1} horizontal="space-between" breakpoints={{ 768: 'column' }}>
Accordion <br></br>
</Row>
<FlipContent>
<FrontSide>
<CardBackgroundComponent title="Testing" image={image}></CardBackgroundComponent>
</FrontSide>
<BackSide>
<SectionedCardComponent
title="Notarum Black"
content="Powerful and reliable, this 15” HD laptop will not let you down. 256GB SSD storage, latest gen."
link=""
linkDescription="Add To Cart"
/>
</BackSide>
</FlipContent>
</Column>
);
}
I think you have not inserted something inside both component FrontSide, BackSide
<div className={`flip-content ${setFlipped}`} onClick={FlippingCard} >
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{setFront ? <FrontSide> It's front side </FrontSide> : null}
{setBack ? <BackSide> It's back-side </BackSide> : null}
</div>
</div>
So in your component you are not rendering children. So you need to update two things.
1) Taking the props in the FlipContent component as shown below
function FlipContent(props)
2) Use the props when rendering inside the component as shown below
{setFront ? <FrontSide>{props.children}</FrontSide> : null}
{setBack ? <BackSide>{props.children} </BackSide> : null}
the problem is in second step is it will load all the props of children , so you need to render only the specific component. See the below one
Update
There are multiple ways to solve this one will list one by one
solution one
By using the name prop of the children
function FlipContent(props) {
const [view, setView] = useState("FrontSide");
function FlippingCard() {
setView(view === "FrontSide" ? "BackSide" : "FrontSide");
}
const component = React.Children.map(props.children, child => {
if (view === child.type.name) {
return child;
}
});
return (
<div className={`flip-content`} onClick={FlippingCard}>
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{component}
</div>
</div>
);
}
Working codesandbox
Solution Two
Instead of adding statically the names can be driven from the prop, this can't handle same component multiple times
function FlipContent(props) {
const [view, setView] = useState(props.children[0].type.name);
const ref = useRef(0);
function FlippingCard() {
if (props.children.length - 1 === ref.current) {
ref.current = 0;
setView(props.children[0].type.name);
return;
}
setView(props.children[ref.current + 1].type.name);
ref.current += 1;
}
let component = <span />;
React.Children.forEach(props.children, child => {
if (view === child.type.name) {
component = child;
return;
}
});
return (
<div className={`flip-content`} onClick={FlippingCard}>
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{component}
</div>
</div>
);
}
Working codesandbox
Solution three
Rendering multiple components and in the same wrapper itself.
function FlipContent(props) {
const [component, setComponent] = useState(props.children[0]);
const ref = useRef(0);
function FlippingCard() {
if (props.children.length - 1 === ref.current) {
ref.current = 0;
setComponent(props.children[0]);
return;
}
setComponent(props.children[ref.current + 1]);
ref.current += 1;
}
return (
<div className={`flip-content`} onClick={FlippingCard}>
<div className="flip-content-container" style={{ cursor: "pointer" }}>
{component}
</div>
</div>
);
}
Working codesandbox
I feel solution three is the simplest one and you have the scalable way.
Fairly new with react. I'm creating a dropdown button for a Gatsby project. The button toggle works, but I'm having trouble getting the selected value to the parent where I need it.
-Tried lifting the state up, but this resulted in the button not appearing at all. I was a bit confused here so maybe I was doing something wrong.
-Also tried using refs although I wasn't sure if this was the right use case, it worked, however it seems the value is grabbed before it's updated in the child component and I'm not sure how to change or work around this. (the code is currently set up for this)
Are either of these options right? or could anybody steer me in the right direction, thanks.
Dropdown in parent:
this.dropdownRef1 = React.createRef();
componentDidUpdate(){
console.log("Color Option:" + this.dropdownRef1.current.state.ColorOption)
}
<DropdownBtn ref={this.dropdownRef1} mainText="Color" options={this.props.pageContext.colors || ['']} />
DropdownBtn:
export default class refineBtn extends React.Component {
constructor(props) {
super(props);
}
state = {
open: false,
[this.props.mainText + "Option"]: "all",
};
dropdownBtnToggle = () => {
this.setState((prevState)=> {
return{open: !prevState.open};
});
};
optionClickHandler = (option) => {
this.setState(() => {
console.log(this.props.mainText + " updated to " + option)
return {[this.props.mainText + "Option"] : option}
});
};
render(){
const options = this.props.options
console.log("open: " + this.state.open)
return(
<div>
<button onClick={this.dropdownBtnToggle} >
{this.props.mainText}:
</button>
<div className={this.state.open ? 'option open' : "option"} >
<p key={"all"} onClick={() => this.optionClickHandler("all")}> all</p>
{options.map(option => (
<p key={option} onClick={() => this.optionClickHandler(option)}>{option}</p>
))}
</div>
</div>
);
}
}
You can respond to selection by allowing your component to accept a callback.
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {open: false, value: ''}
}
render() {
return (
<div>
<div onClick={() => this.setState({open: true})}>{this.state.value}</div>
<div style={{display: this.state.open ? 'block' : 'none'}}>
{this.props.options.map((option) => {
const handleClick = () => {
this.setState({open: false, value: option})
this.props.onChange(option)
}
return (
<div key={option} onClick={handleClick} className={this.state.value === option ? 'active' : undefined}>{option}</div>
)
})}
</div>
</div>
)
}
}
<MyComponent onChange={console.log} options={...}/>
I am using a json with several values, one of them is "iframe" which can be "si" (yes) or "no" depending on whether it is an iframe or not.
With that value (yes / no) I need (this.props.tabsiframe === 'yes') to show an <iframe> or a <div>.
My code works, because if (this.props.tabsiframe === 'yes') is an iframe it paints an iframe, but if the next element is (this.props.tabsiframe === 'no' ) change all the elements of <iframe> to <div>.
The re-rendering changes the already created divs and converts them to iframe if iframe = yes and if it is = it does not change the iframe to div. This is an example of what happens
https://drive.google.com/file/d/1cY9eHq_aBuQb8b_HtYUXjC6lqrYCJe3p/view
Maybe it happens because every time I click on the element the menu is updated: divIframe: {tabsDivIframe: [... new Set (this.state.divIframe.tabsDivIframe), url] .filter (function (el) { return the;})},. A new url is added to the array, so I guess that's what it renders and it changes the divs to iframes or iframes to divs.
class App extends Component {
constructor(props, context){
super(props, context);
["openTabs", "removeTab"].forEach((method) => {
this[method] = this[method].bind(this);
});
this.state = {
tabs:{
tabsLi: [],
},
divIframe:{
tabsDivIframe: [],
},
tabsiframe : '',
showtabs: true,
}
}
openTabs(e, url, iframe, trdtitle){
e.preventDefault();
this.setState({
showtabs: false,
})
if (this.state.tabs.tabsLi.includes(trdtitle) === false){
this.setState({
tabs: { tabsLi:[...new Set(this.state.tabs.tabsLi),trdtitle].filter(function(el) { return el; })},
divIframe: { tabsDivIframe:[...new Set(this.state.divIframe.tabsDivIframe),url].filter(function(el) { return el; })},
tabsiframe: iframe,
}, () => {
console.log(this.state.tabs.tabsLi);console.log(this.state.divIframe.tabsDivIframe);console.log(this.state.tabsiframe)
})
}
}
render(){
return (
<><Tabs
showtabs={this.state.showtabs}
tabs={this.state.tabs}
tabsLi={this.state.tabs.tabsLi}
divIframe={this.state.divIframe}
tabsDivIframe={this.state.divIframe.tabsDivIframe}
tabsiframe={this.state.tabsiframe}
openTabs={this.openTabs}
removeTab={this.removeTab}
/>
</>
)
}
}
class Tabs extends Component {
render(){
return(
<div id="content-tabs" className="tabs">
{( this.props.showtabs)
? (
<>
<div className="waiting-leads">
<p>Parece que todavía no hay ningún lead...</p>
<h3>¡Ánimo, ya llega!</h3>
<img src={imgDinosaurio} alt="Dinosaurio"></img>
</div>
</>
) : (
<>
<DivAndIframe
tabsDivIframe={this.props.divIframe.tabsDivIframe}
tabsiframe={this.props.tabsiframe}
/>
</>
)}
</div>
);
}
}
class DivAndIframe extends Component{
render(){
return(
<>
{this.props.tabsDivIframe.map((url, index) =>
<div key={url.toString() id={"myTab" + index}>
{( this.props.tabsiframe === 'si')
? (
<iframe title={"iframe"+index} className="iframeTab" src={url}></iframe>
) : (
<div>{url}</div>
)}
</div>
)}
</>
);
}
}
The problem you are having here is within your map.
Here is a state map to try to explain:
After First tab Click (div):
this.props.tabsiframe = 'no'
this.props.tabsDivIframe = ['www.sampleurl1.com']
After Second tab Click (iframe):
this.props.tabsiframe = 'yes'
this.props.tabsDivIframe = ['www.iframe2.com', 'www.sampleurl1.com']
so on the virtual DOM after the second click, the map will again iterate through this.props.tabDivIframe and evaluate this.props.tabsiframe as 'si' or 'no' for everything.
{this.props.tabsDivIframe.map((url, index) =>
<div key={url.toString() id={"myTab" + index}>
{( this.props.tabsiframe === 'si') //this line here; we share this same value for all tabs
? (
<iframe title={"iframe"+index} className="iframeTab" src={url}></iframe>
) : (
<div>{url}</div>
)}
</div>
)}
I wrote a small interactive sample of your problem here: https://jsfiddle.net/eojrx6my/6/
You will need to store individual 'tabsiframe' for each url, or somehow identify them individually
I have a fade-in animation I want to use for a particular parent component. The problem is, the animation only occurs the first time the component is rendered. When I change the state by clicking a button, the animation does not reset and get triggered again.
This is in a react-redux application. I haven't tried adding an action and reducer and then utilizing componentDidMount(). Any help would be greatly appreciated.
Here's the component that renders the data:
export class Doctors extends React.Component {
componentDidMount() {
this.props.dispatch(fetchProfileInfo(this.props.user.id));
}
render() {
if (this.props.profile.doctors) {
const cards = this.props.profile.doctors.map((d, i) => {
return {
previous: i === 0 ? null : i - 1,
next: i === this.props.profile.doctors.length - 1 ? null : i + 1,
doctor: d
};
});
const d = cards[this.props.currentDoctor].doctor;
console.log('this.props.profile.doctors', this.props.profile.doctors);
return (
<div className="container">
<NavigationBar />
<main role="main">
<h1 className="doctors-h1">Doctors</h1>
<div className="doctor-display-section">
<Doctor doctor={d} />
</div>
<div className="doctor-button-holder">
<button
className={this.props.currentDoctor !== 0 ? 'display-doctor-button-1' : 'hidden-1'}
onClick={() => this.props.dispatch(updateCurrentDoctor(cards[this.props.currentDoctor].previous))}
>
<p className="fas fa-long-arrow-alt-left"></p>
<p className="display-profile-section-button-p">
{cards[this.props.currentDoctor].previous === null ? '' : cards[this.props.currentDoctor - 1].doctor.name.firstName +
' ' + cards[this.props.currentDoctor - 1].doctor.name.lastName}
</p>
</button>
<button
className={this.props.currentDoctor !== cards.length - 1 ? 'display-doctor-button-2' : 'hidden-1'}
onClick={() => this.props.dispatch(updateCurrentDoctor(cards[this.props.currentDoctor].next))}
>
<p className="fas fa-long-arrow-alt-right"></p>
<p className="display-profile-section-button-p">
{cards[this.props.currentDoctor].next === null ? '' : cards[this.props.currentDoctor + 1].doctor.name.firstName +
' ' + cards[this.props.currentDoctor + 1].doctor.name.lastName}
</p>
</button>
</div>
</main>
<Footer />
</div>
);
}
return (
<div>
loading...
</div>
);
}
}
const mapStateToProps = state => ({
user: state.auth.currentUser,
profile: state.app.profile,
currentDoctor: state.app.currentDoctor
});
export default requiresLogin()(connect(mapStateToProps)(Doctors));