I am trying to make a modal reusable:
this is my component:
class OverleyModal extends Component {
constructor(props) {
super(props);
}
openModal = () => {
document.getElementById("myOverlay").style.display = "block";
}
closeModal = () => {
document.getElementById("myOverlay").style.display = "none";
}
render() {
return (
<React.Fragment>
<div id="myOverlay" className="overlay">
<div className="overlay-content">
<p>content goes there</p>
</div>
</div>
</React.Fragment>
)
}
}
export default OverleyModal;
The above component is working great for the purpose of modal, that is why i didn't include here CSS/style, the issue not about CSS.
I want, when i mount this component on any compoenet like thise below:
<overleyModal open={true} />
if open=true, the modal will be visiable
<overleyModal open={false} />
and if open={false}
the modal will disappear
You can see how i deal open and close modal in the coponent method openModal() and closeModal()
But i am going through the trouble to make it reliable, I just want to use open as props, nothing else. if open is true, it will appear and if false, it will disappear.
Can anyone please help me in this case?
You need to make use of props and control the opening and closing through it by conditionally rendering it. You can also make the content generic by passing it as props too
class OverlayModal extends Component {
render() {
const { open, content } = this.props
return open? <React.Fragment>
<div id="myOverlay" className="overlay">
<div className="overlay-content">
<p>{content}</p>
</div>
</div>
</React.Fragment>: null
}
}
export default OverlayModal;
and use it like
<OverlayModal open={true} content={content goes there'} />
or even better you can define the content as children to give you more styling options
class OverlayModal extends Component {
render() {
const { open, children} = this.props
return open? <React.Fragment>
<div id="myOverlay" className="overlay">
<div className="overlay-content">
{children}
</div>
</div>
</React.Fragment>: null
}
}
export default OverlayModal;
and using as
<OverlayModal open={true} ><p>content goes there</p></OverlayModal >
open can be a property value and modal can be rendered conditionally based on the prop value. There is no need to directly access dom element.
return props.open ? (
<div id="myOverlay" className="overlay">
<div className="overlay-content">
<p>content goes there</p>
</div>
</div>
) : null;
Related
I have in React an accordion with buttons that slide up/down content, like on this image
I made some functional components and things look like this:
<SlideButtonContainer>
<SlideButton title="Title goes here">
Content goes here
</SlideButton>
<SlideButton title="Title goes here">
Content goes here
</SlideButton>
</SlideButtonContainer>
Clicking a SlideButton calls a function handleClick that changes the state opened of the component, making it slide its content up on false or down on true.
But how could I make all the other SlideButtons inside SlideButtonContainer set their state opened to false?
SlideButton uses SlideDown from this repo and is as follows:
export default function SlideButton(props) {
const [opened, setOpened] = useState(false);
function handleClick() {
setOpened(!opened);
}
return (
<div className="slideButton">
<div className="slideButtonButton" onClick={handleClick}>
<div>
{props.title}
</div>
</div>
<div className="slideButtonContents">
<SlideDown>
{opened ? props.children : null}
</SlideDown>
</div>
</div>
);
}
SlideButtonContainer is just a styled div
export default function SlideButtonContainer(props) {
return (
<div className="slideButtonContainer">
{ props.children }
</div>
);
}
Solved.
In SlideButtonContainer I use React.cloneElement on render and set refs to its children, and I can use children's function with this loop:
for (var ref in this.refs) {
this.refs[ref].slideUp();
}
I have a React js App (No JQuery please) drop down menu that I trigger when the ▼ character is clicked. With my code below, the dropdown menu disappears only when the same ▼ is clicked again. I want to make it disappear when the user clicks anywhere else on the page. How to change the code below to make the dropdown menu disappear when the user clicks anywhere else on the page as well and not just by clicking the same icon ▼ ?
Toggler Icon:
<span className="show_more" onClick={this.toggleOptions}>
<MaterialIcon icon="keyboard_arrow_down" />
</span>
Code for toggling: (used by many components so can the fix be only here ?)
import React, { Component } from 'react'
...
import MaterialIcon from '../icons/material-icon'
import HeaderLogo from './logo'
export default class Header extends Component {
state = {
showOptions: false,
}
toggleOptions = () => this.setState({ showOptions: !this.state.showOptions })
render() {
let { showOptions } = this.state
return (
<div className="header">
<div className="row-container container">
<div className="col-l">
<HeaderLogo />
</div>
<div className="col-m">
<Search />
</div>
<div className="col-r">
<div className="header_right">
<HeaderTopLinks />
<span className="show_more" onClick={this.toggleOptions}>
<MaterialIcon icon="keyboard_arrow_down" />
</span>
</div>
{showOptions ? (
<HeaderOptions toggleOptions={this.toggleOptions} />
) : null}
</div>
</div>
</div>
)
}
}
The answer can be found here
But to sum it up, you have to listen for clicks on the document, and write a function that will walk the tree and tell you if the click occurred inside or outside your component
Here are the important bits from that link to add to your component:
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
alert('You clicked outside of me!');
this.setState({ showOptions: false });
}
}
componentDidMount() {
document.addEventListener('mousedown', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside);
}
render() {
let { showOptions } = this.state;
return <div className="header" ref={(node) => this.setWrapperRef = node}>...all the rest of the component goes here...</div>;
}
For the record, there are many ways of accomplishing this, this is just one approach.
I am working on implementing "invite someone" functionality on a webapp. I have a "invite" button. Now I want that whenever someone clicks on it, a popup should apear on screen asking for an email id as input, which I can use to send an invite link. So I am not sure how to have that popup with an input field.
Apologies if someone find this question too basic. I am pretty new to react.js
You can handle the open and close state of your popup with the component state. You can either use css or Javascript to show or hide the popup.
If you use CSS you need to pass a css class to the popup like so:
class App extends Component {
state = {
open: false
}
handlePopUp(){
this.setState({open: !this.state.open})
}
render() {
return (
<div className="App">
<button onClick={() => this.handlePopUp()}>Open / close</button>
<div className={`modal ${this.state.oepn}`}>
{/* form content here */}
</div>
</div>
);
}
}
If you are using Javascript you will need to have a conditional like so:
class App extends Component {
state = {
open: false
}
handlePopUp(){
this.setState({open: !this.state.open})
}
render() {
return (
<div className="App">
<button onClick={() => this.handlePopUp()}>Open / close</button>
{
this.state.oepn ?
<div className="modal">
{/* form content here */}
</div>
: null
}
</div>
);
}
}
export default App;
I would recommend handling it with CSS so you can do some nice css transitions
I'm new to React and I'm puzzled on something kind of basic.
I need to append a component to the DOM after the DOM is rendered, on a click event.
My initial attempt is as follows, and it doesn't work. But it's the best thing I've thought to try. (Apologies in advance for mixing jQuery with React.)
ParentComponent = class ParentComponent extends React.Component {
constructor () {
this.addChild = this.addChild.bind(this);
}
addChild (event) {
event.preventDefault();
$("#children-pane").append(<ChildComponent/>);
}
render () {
return (
<div className="card calculator">
<p><a href="#" onClick={this.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
<ChildComponent/>
</div>
</div>
);
}
};
Hopefully it's clear what I need to do, and I hope you can help me attain an appropriate solution.
Don't use jQuery to manipulate the DOM when you're using React. React components should render a representation of what they should look like given a certain state; what DOM that translates to is taken care of by React itself.
What you want to do is store the "state which determines what gets rendered" higher up the chain, and pass it down. If you are rendering n children, that state should be "owned" by whatever contains your component. eg:
class AppComponent extends React.Component {
state = {
numChildren: 0
}
render () {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<ChildComponent key={i} number={i} />);
};
return (
<ParentComponent addChild={this.onAddChild}>
{children}
</ParentComponent>
);
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
}
const ParentComponent = props => (
<div className="card calculator">
<p><a href="#" onClick={props.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
{props.children}
</div>
</div>
);
const ChildComponent = props => <div>{"I am child " + props.number}</div>;
As #Alex McMillan mentioned, use state to dictate what should be rendered in the dom.
In the example below I have an input field and I want to add a second one when the user clicks the button, the onClick event handler calls handleAddSecondInput( ) which changes inputLinkClicked to true. I am using a ternary operator to check for the truthy state, which renders the second input field
class HealthConditions extends React.Component {
constructor(props) {
super(props);
this.state = {
inputLinkClicked: false
}
}
handleAddSecondInput() {
this.setState({
inputLinkClicked: true
})
}
render() {
return(
<main id="wrapper" className="" data-reset-cookie-tab>
<div id="content" role="main">
<div className="inner-block">
<H1Heading title="Tell us about any disabilities, illnesses or ongoing conditions"/>
<InputField label="Name of condition"
InputType="text"
InputId="id-condition"
InputName="condition"
/>
{
this.state.inputLinkClicked?
<InputField label=""
InputType="text"
InputId="id-condition2"
InputName="condition2"
/>
:
<div></div>
}
<button
type="button"
className="make-button-link"
data-add-button=""
href="#"
onClick={this.handleAddSecondInput}
>
Add a condition
</button>
<FormButton buttonLabel="Next"
handleSubmit={this.handleSubmit}
linkto={
this.state.illnessOrDisability === 'true' ?
"/404"
:
"/add-your-details"
}
/>
<BackLink backLink="/add-your-details" />
</div>
</div>
</main>
);
}
}
I am trying to code a react component for a modal and getting the error: "Uncaught TypeError: this.setState is not a function" when the openModal() is called. Below is my code:
class Header {
constructor() {
this.state = { showModal: false };
}
openModal() {
this.setState({showModal: true});
}
closeModal() {
this.setState({showModal: false});
}
render() {
return (
<div className="Header">
<ModalDialog heading="heading" show={this.state.showModal}>
<p>Body</p>
</ModalDialog>
<div className="Header-container">
<Navigation className="Header-nav" />
<div className="Navigation2">
<Button bsStyle='primary' onClick={this.openModal.bind(this)}>Sign in</Button>
<Button bsStyle='info' href="/register" onClick={Link.handleClick}>Sign up</Button>
</div>
</div>
</div>
);
}
}
class ModalDialog {
render() {
if (this.props.show) {
return (
<div className="Overlay">
<Modal heading={this.props.heading}>
<div>{this.props.children}</div>
</Modal>
</div>
);
}
return null;
}
};
class Modal {
render() {
return (
<div className="Modal effect">
<h3>{this.props.heading}</h3>
<div className="content">
{this.props.children}
</div>
</div>
);
}
};
I am also trying to pass in a className into the element <div className="Modal effect"> from the Header component to change the dimensions of the Modal, is this possible?
Would be great if somebody could help me out with this. I am just starting out with React. Thanks
I am also trying to pass in a className into the element from the Header component to change the dimensions of the Modal, is this possible?
This is typically what props are made for, passing static information such size.
So simply pass your class/size/staticprops as a prop like this:
<ModalDialog size={//whatever you need} className="Modal effect"/>
From ModalDialog pass it to Modal
<Modal size={this.props.size} />
And then you just have to use it inside your Modal for whatever purpose you want. More about props in general :https://facebook.github.io/react/docs/transferring-props.html
Hope it helps
I'm not familiar with reactJS but this looks wrong to me:
constructor() {
this.state = { showModal: false };
}
Should it be:
constructor() {
this.setState = ({ showModal: false });
}
?
Otherwise your Header class sets its state attribute to { showModal: false }
Which obviously has no 'setState' member function.