Rendering a modal in React - javascript

I'm trying to trap an onclick method on a React component to create a React Modal.
I've added react-overlay as a dependency and added it to my file.
import Modal from 'react-overlays';
This is the anchor element,
<a href="#" onClick={this.handleClick} data-id={image.id}>
This is the handleclick method,
handleClick(event) {
event.preventDefault();
let mediaId = event.currentTarget.attributes['data-id'].value;
this.setState({ overlay: <Modal show={this.state.showModal} onHide={this.close} mediaId={mediaId}/> });
}
I get the following error,
Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.(…)

I recently had this problem and got around it by creating a Modal-component.
import Modal from 'react-modal'
export default class CustomModal extends React.Component {
constructor () {
super();
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
this.state = {
open: false
}
}
openModal () { this.setState(
{open: true});
$(function(){
$("#custom-modal").appendTo("body");
});
}
closeModal () {
this.setState({open: false});
}
componentDidMount(){
$(function(){
$("#custom-modal").appendTo("body");
});
}
render () {
return (
<div>
<button onClick={this.openModal}>My modal</button>
<Modal id="custom-modal" isOpen={this.state.open} onRequestClose={this.closeModal}>
// Modal body content here
<button onClick={this.closeModal}>Close</button>
</Modal>
</div>
);
}
}
And then using it like this:
import CustomModal from '../components/CustomModal'
...
<li><CustomModal/></li>
Hope this is of any help.

Your state should contain information that allows you to render the modal, but not the modal itself.
It's highly unusually to store components in state.
Try this:
Store a flag in your state to indicate whether the modal should be shown.
Set the flag in handleClick()
In render(), render the modal if the flag is set.
Let me know if you need an example.

Looks like you're importing undefined..
Also, take a look at https://github.com/fckt/react-layer-stack. This is universal and clean way to solve the "modal problem" in React. Demo - https://fckt.github.io/react-layer-stack/

In case anyone still has an issue with this, a modern approach is to build the modal using React hooks. as shown below
import React from 'react';
import './modal.css';
import FontAwesome from 'react-fontawesome';
const Modal = (props) => {
const { closeModal } = props;
const closeicon = () => (
<FontAwesome
name="times"
onClick={closeModal}
style={{
color: '#000000',
padding: '10px',
cursor: 'pointer',
backgroundColor: 'transparent',
border: 0,
position: 'absolute',
top: '0.3rem',
right: '0.5rem',
}}
/>
);
return (
<div className="overlay">
<div className="content">
{ closeicon() }
{props.children}
</div>
</div>
);
};
export default Modal;
The css is as shown below
.overlay {
position: fixed;
display: block;
overflow: auto;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5);
z-index: 999;
cursor: pointer;
}
.content {
margin: 15% auto;
background-color: white;
border-radius: 0.25rem;
width: 50vw;
padding: 2rem;
position: relative;
}
So you can use the modal component like this
const [status, setStatus] = useState(false);
//this button will trigger the modal
<button onClick={() => setStatus(true)}>Open Modal</button>
{
status && (
<Modal closeModal={() => setStatus(false)}><p>hello worls</p></Modal>
)
}
No need to worry about responsiveness It's been taken care of in the styling.
For further explanation, you can check this link
https://dev.to/adeyemiadekore2/how-to-build-a-reusable-and-responsive-modal-in-react-from-scratch-1o0f

Related

How to call a function with arguments onclick of a button in a react component

Here is my function with arguments that i added in index.html in publics folder in a script tag
function displayContent(event, contentNameID) {
let content = document.getElementsByClassName("contentClass");
let totalCount = content.length;
for (let count = 0; count < totalCount; count++) {
content[count].style.display = "none";
}
let links = document.getElementsByClassName("linkClass");
totalLinks = links.length;
for (let count = 0; count < totalLinks; count++) {
links[count].classList.remove("active");
}
document.getElementById(contentNameID).style.display = "block";
event.currentTarget.classList.add("active");
}
Trying to call this function from click of buttons on my react component that looks like below
<button class="linkClass" onclick="displayContent(event, 'project2')">Meet at Campus
</button>
Please guide me with the syntax
Here's the correct syntax
<button className="linkClass" onClick={(event)=>displayContent(event,'project2')}>Meet at Campus</button>
Edit: please note that React components return JSX
It looks like you're trying to make some sort accordion but you shouldn't really be mixing vanilla JS with React as React needs control of the DOM.
So here's a brief example of how you might approach this using 1) state, and 2) a Panel component which comprises a button, and some content.
const { useState } = React;
function Example() {
// Initialise state with an array of false values
const [ state, setState ] = useState([
false, false, false
]);
// When a button in a panel is clicked get
// its id from the dataset, create a new array using `map`
// and then set the new state (at which point the component
// will render again
function handleClick(e) {
const { id } = e.target.dataset;
const updated = state.map((el, i) => {
if (i === id - 1) return true;
return false;
});
setState(updated);
}
// Pass in some props to each Panel component
return (
<div>
<Panel
name="Panel 1"
active={state[0]}
id="1"
handleClick={handleClick}
>
<span className="text1">Content 1</span>
</Panel>
<Panel
name="Panel 2"
active={state[1]}
id="2"
handleClick={handleClick}
>
<span className="text2">Content 2</span>
</Panel>
<Panel
name="Panel 3"
active={state[2]}
id="3"
handleClick={handleClick}
>
<span className="text3">Content 3</span>
</Panel>
</div>
);
}
function Panel(props) {
// Destructure those props
const {
name,
id,
active,
handleClick,
children
} = props;
// Return a div with a button, and
// content found in the children prop
// When the button is clicked the handler is
// called from the parent component, the state
// is updated, a new render is done. If the active prop
// is true show the content otherwise hide it
return (
<div className="panel">
<button data-id={id} onClick={handleClick}>
{name}
</button>
<div className={active && 'show'}>
{children}
</div>
</div>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
.panel button:hover { cursor: pointer; }
.panel { margin: 1em 0; }
.panel div { display: none; }
.panel div.show { display: block; margin: 1em 0; }
.add { margin-top: 1em; background-color: #44aa77; }
.text1 { color: darkblue; font-weight: 600; }
.text2 { color: darkgreen; font-weight: 700; }
.text3 { color: darkred; font-weight: 300; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Can't you use
document.getElementById("linkClass").onclick = () =>{
displayContent();
}
by giving the element an id with same of the class?

How to change css property in react

I want to change css width property of my element on some condition
<div className="consoleLayoutRoot-sideMenu">
<ConsoleSideMenu />
</div>
css
.consoleLayoutRoot-sideMenu .ant-menu {
padding-top: 30px;
/* background-color: #191146 !important; */
}
I am doing this way..but nothing is happening
document.getElementsByClassName("conjnjnot-sideMenjnjbhbhu.annjn ").style.width = "77px";
That's not working because you're treating a list as though it were an element. But it's also fundamentally not how you would do this in a React project.
Instead, you'd have the component re-render when the condition becomes true (perhaps by setting a state member). When rendering the div, you optionally include a style or a class name depending on whether you want the width applied:
<div className={`consoleLayoutRoot-sideMenu ${shouldHaveWidthClass ? "width-class" : ""}`}>
<ConsoleSideMenu />
</div>
...where .width-class { width: 50px; } is in your stylesheet.
Or with inline style, but inline styles are best avoided:
<div className="consoleLayoutRoot-sideMenu" style={shouldHaveWidthSetting ? { width: "50px" } : undefined}>
<ConsoleSideMenu />
</div>
Here's an example (using a class);
const {useState} = React;
const ConsoleSideMenu = () => <span>x</span>;
const Example = () => {
const [includeWidth, setIncludeWidth] = useState(false);
const toggle = ({currentTarget: { checked }}) => {
setIncludeWidth(checked);
};
return <React.Fragment>
<div className={`consoleLayoutRoot-sideMenu ${includeWidth ? "width-class" : ""}`}>
<ConsoleSideMenu />
</div>
<label>
<input type="checkbox" onChange={toggle} checked={includeWidth} />
Include width class
</label>
</React.Fragment>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
.width-class {
width: 50px;
}
.consoleLayoutRoot-sideMenu {
border: 1px solid blue;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Is it appropriate to use Document in the react framework?

I have this React component. Which renders a simple HTML. I have an event handler attached to an element. On clicking that particular element I want some CSS styles to change. For that I used the code below-
import React from 'react';
import './start.css';
class Start extends React.Component {
handleEvent() {
const login = document.querySelector('.login');
const start = document.querySelector('.start')
login.style.right = '0';
start.style.left = '-100vw';
}
render() {
return (
<section className = 'page start'>
<h1>Welcome To Our App</h1>
<button onClick = {this.handleEvent}>Next</button>
</section>
)
}
}
export default Start;
My question is in the handleEvent() is it appropriate to select the elements using Document and style the elements using .style. Is there any other "react-specific" way to do this?
class Test extends React.Component {
constructor(){
super();
this.state = {
black: true
}
}
changeColor(){
this.setState({black: !this.state.black})
}
render(){
let btn_class = this.state.black ? "blackButton" : "whiteButton";
return (
<div>
<button className={btn_class}
onClick={this.changeColor.bind(this)}>
Button
</button>
</div>
)
}
}
ReactDOM.render(<Test />, document.querySelector("#app"))
button{
width: 80px;
height: 40px;
margin: 15px;
}
.blackButton{
background-color: black;
color: white;
}
.whiteButton{
background-color: white;
color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
first of all yes you can use document in react for that. But "react specific" style you be something like this:
<div id="app"></div>
In css file :
button{
width: 80px;
height: 40px;
margin: 15px;
}
.blackButton{
background-color: black;
color: white;
}
.whiteButton{
background-color: white;
color: black;
}
and finally a component :
class Test extends React.Component {
constructor(){
super();
this.state = {
black: true
}
}
changeColor(){
this.setState({black: !this.state.black})
}
render(){
let btn_class = this.state.black ? "blackButton" : "whiteButton";
return (
<div>
<button className={btn_class}
onClick={this.changeColor.bind(this)}>
Button
</button>
</div>
)
}
}
ReactDOM.render(<Test />, document.querySelector("#app"))
You can set a state to check whether the button has been clicked and change the class name
Similar approach can be used.
This is the React Specific way!
You can refer to React doc
https://reactjs.org/docs/faq-styling.html
import React from 'react';
import './start.css';
class Start extends React.Component {
constructor(props) {
super(props);
this.state = {hasButtonClicked : false};
}
handleEvent() {
this.setState({hasButtonClicked : true});
}
render() {
let clicked = this.state.hasButtonClicked;
return (
<section className = { clicked ? someCssClass :'page start'} >
<h1>Welcome To Our App</h1>
<button onClick = {this.handleEvent}>Next</button>
</section>
)
}
}

React component that zooms into an image while keeping dimensions on mouse over - React + Bootstrap

So I have been looking all over the place over the past few days and cannot find anything that works. I am using React with Bootstrap. I want a stateless functional component that takes an image path and returns an img element which, when hovered over by the mouse, zooms into the image while keeping the dimensions of the element the same.
I have tried:
Changing the style attribute in the onMouseOver and onMouseOut events like so
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<img
src={imagePath}
alt=""
style={{ overflow: "hidden" }}
onMouseOver={(e) => (e.currentTarget.style = { transform: "scale(1.25)", overflow: "hidden" })}
onMouseOut={(e) => (e.currentTarget.style = { transform: "scale(1)", overflow: "hidden" })}
/>
);
};
export default ImageHoverZoom;
Creating a custom css class and applying that to the img element.
index.css:
.hover-zoom {
overflow: hidden;
}
.hover-zoom img {
transition: all 0.3s ease 0s;
width: 100%;
}
.hover-zoom img:hover {
transform: scale(1.25);
}
imageHoverZoom.jsx:
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<img
src={imagePath}
alt=""
className="hover-zoom"
/>
);
};
export default ImageHoverZoom;
I have also tried a class component with state
import React, { Component } from "react";
class ImageHoverZoom extends Component {
state = {
path: this.props.imagePath,
mouseOver: false,
};
render() {
const { path, mouseOver } = this.state;
return (
<img
className={`img-fluid w-100`}
src={path}
onMouseOver={() => this.setState({ mouseOver: true })}
onMouseOut={() => this.setState({ mouseOver: false })}
style={
mouseOver
? { transform: "scale(1.25)", overflow: "hidden"}
: { transform: "scale(1)", overflow: "hidden"}
}
alt=""
/>
);
}
}
I would ideally not like to use state as I know it gets updated asynchronously and I feel like that would lead to some lag on the client side when mousing over the image. Any help is much obliged, thank you in advance!
EDIT:
I tried Rahul's answer below in my project, as well as in a brand new project. Here are the relevant (I think) files. Same thing as before. No change on mouse over.
App.js
import "./App.css";
import ImageHoverZoom from "./common/imageHoverZoom";
function App() {
return <ImageHoverZoom imagePath="http://picsum.photos/400/600" />;
}
export default App;
imageHoverZoom.jsx
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img src={imagePath} alt="" className="hover-zoom" />
</div>
);
};
export default ImageHoverZoom;
index.css
.img-wrapper {
overflow: hidden;
}
.hover-zoom img:hover {
transform: scale(1.25);
}
Wrap the img tag in a div and then hide the overflow from div:
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img
src={imagePath}
alt=""
className="hover-zoom"
/>
</div>
);
};
export default ImageHoverZoom;
add the styling to img-wrapper:
.img-wrapper{
overflow:hidden;
}
img.hover-zoom:hover {
transform: scale(1.25);
}
So I solved it with Rahul's help (Thank you!). The css notation was flipped, like Rahul suggested, and to prevent the width from changing I had to set width: 100% in img.hover-zoom
Here is the component:
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img src={imagePath} alt="" className="hover-zoom" />
</div>
);
};
export default ImageHoverZoom;
index.css:
.img-wrapper {
overflow: hidden;
}
img.hover-zoom {
transition: all 0.3s ease 0s;
width: 100%;
}
img.hover-zoom:hover {
transform: scale(1.25);
}

Styling react component in styled components

I have a question with styled components, I would like to know how to position elements from their parents, I saw that there are the following options but I do not like any.
Through props, I don't like this method because I consider that the maintainability of this is horrible since in complex cases we will have many props.
Through className, generally in styled components we don't have class since we create styled.div for example, I like to have consistency in my structure and I don't want to have class names in some and not in others.
In this case CurrentFinderLocationButton is a react component, how would you position them? Is there a way to select it and apply styles from StyledHome without className or props?
import React from "react";
import styled from "styled-components";
import CurrentLocationFinderButton from "../buttons/CurrentLocationFinderButton";
import fullLogotype from "../../assets/images/full-logotype.svg";
const Home = () => {
return (
<StyledHome>
<StyledLogotype src={fullLogotype} />
<CurrentLocationFinderButton />
</StyledHome>
);
};
const StyledHome = styled.div`
`;
const StyledLogotype = styled.img`
width: 150px;
display: block;
margin: auto;
`;
export default Home;
you can just add some styles to wrapper
const StyledCurrentLocationFinderButton = styled(CurrentLocationFinderButton)`
{any styles}
`
Finally i solved this problem binding the component class and styled components class through the props.
import React from "react";
import styled from "styled-components";
import fullLogotype from "../../assets/images/full-logotype.svg";
import CurrentLocationFinderButton from "../buttons/CurrentLocationFinderButton";
import AddressFinder from "../finders/AddressFinder";
const Logotype = ({ className }) => {
return <img className={className} alt="" src={fullLogotype} />;
};
const EntryText = ({ className }) => {
return (
<p className={className}>
Atisbalo es una app donde podrás encontrar información sobre tus locales
favoritos y enterarte de todas las promociones que oferta tu cuidad al
instante
</p>
);
};
const Home = ({ className }) => {
return (
<StyledHome className={className}>
<StyledLogotype />
<StyleEntryText />
<StyledCurrentLocationFinderButton />
<StyledAddressFinder/>
</StyledHome>
);
};
const StyledHome = styled.div``;
const StyledLogotype = styled(Logotype)`
width: 150px;
display: block;
margin: auto auto 30px auto;
`;
const StyleEntryText = styled(EntryText)`
display: block;
width: 90%;
text-align: center;
margin: auto auto 30px auto;
`;
const StyledCurrentLocationFinderButton = styled(CurrentLocationFinderButton)`
display: block;
margin: auto auto 30px auto;
`;
const StyledAddressFinder = styled(AddressFinder)`
width: 80%;
margin: auto;
`
export default Home;

Categories