React 16 - unable to make a modal render using createPortal - reactjs

I'm trying to render a loading notification modal while a POST is processing.
It seems like it should be very simple using createPortal but the modal never displays.
I added a div in index with an id of modal:
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<div id="modal"></div>
</body>
Here is the react component. When the test button is clicked the state of showModal is set to true:
import React, { Component, Header } from 'react';
import { render } from "react-dom";
import { Row, Col, Button } from 'reactstrap'
import 'bootstrap/dist/css/bootstrap.css';
import ProcessingModal from '../Modals/ProcessingModal';
export class TestModal extends React.Component {
constructor(props) {
this.state = {
showModal: false,
}
this.handleShowModal = this.handleShowModal.bind(this);
}
handleShowModal() {
this.setState({ showModal: true });
}
render() {
return (
<div>
<div className='container-fluid'>
<h2 style={{ textAlign: 'center', border: 'none' }}>
Header Text
</h2>
<br />
{this.state.showModal ? <ProcessingModal /> : null }
<Row>
<Col md={6}>
<Button onClick={this.handleShowModal} type="button">test</Button>
</Col>
</Row>
</div>
</div>
);
}
This is the ProcessingModal js:
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
const ModalContent = (
<div className='modal text-center'>
<div className="spinner-border text-success"><br />
Loading...
</div>
</div>
);
function ProcessingModal(props) {
return ReactDOM.createPortal(ModalContent, document.querySelector("#modal"));
}
export default ProcessingModal;
Where am i going wrong?

I think first argument of createPortal expect a React Element, not React Component.
In ProcessingModal.js, wrap ModalContent into JSX Element:
return ReactDOM.createPortal(<ModalContent/>, document.querySelector("#modal"))

Styling was my issue. The modal style had display: none;
Once that was removed it worked.

Related

Why does react display my functional component at the bottom of my page?

My main page looks like this :
import React from "react";
import { Fragment } from "react";
import ModalA from "../components/Modal/ModalOptionA";
export default class AePage extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Fragment>
<div className="grid-intro">
<div className="text-intro">
Some Text
</div>
<div className="modal-component-insert">
<ModalA show={true}/>
</div>
<div className="text-outro">
Some text
</div>
</div>
</Fragment>
)
}
}
And my component looks like this :
import React from "react";
import ReactDOM from "react-dom";
const Modal = ({ show, closed}) => {
return (
ReactDOM.createPortal(
<>
<div className="modal">
My Component
</div>
</>,
document.body
)
)
}
export default Modal;
The code above display something like :
Some Text
Some Text
My Component
Why does my component not display between the texts ? Is there a specific way for React to display this component between my divs ?
That is what ReactDOM.createPortal is for. It will always move things to the bottom of the DOM. That way, you can then position it above everything else using CSS.
It seems you don't really need that, so I'd just replace your code for the Modal component with:
import React from "react";
const Modal = ({ show, closed}) => {
return (
<div className="modal">
My Component
</div>
)
}
export default Modal;

How to scroll to bottom of div

I have troubles with scrolling down my component. I want scroll bar thumb to be at the very end of chat div from the start. I am not sure what the probleme is here. MessageTo and MessageFrom are components that represent paragraphs with text and display author's name. ChatInput is textarea with send button.
import React from 'react';
import './index.css';
import MessageFrom from './message-from';
import MessageTo from './message-to';
import ChatInput from './chat-input';
import SimpleBarReact from 'simplebar-react';
import 'simplebar/src/simplebar.css';
class Chat extends React.Component {
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate(){
this.scrollToBottom()
}
scrollToBottom = () => {
this.el.scrollIntoView({behavior:"smooth"});
}
render(){
return(
<div className="chat" id="slider-container">
<SimpleBarReact style={{maxHeight: 680}} id="scroll-bar-cont">
<div className="messages-container" id="for-slider" >
<MessageFrom />
<MessageTo />
<div style={{ float:"left", clear: "both" }}
ref={el => {this.el=el;}}>
</div>
</div>
</SimpleBarReact>
<ChatInput />
</div>
);
}
}
export default Chat;
https://codesandbox.io/s/great-currying-x4m8m?file=/src/message-to.js
It works well. But It doesn't work in next level component.

Using fullpagejs in React, how to trigger function on active slide without re-rendering entire page

In my React app I am using fullpage.js to render two slides containing two different components. I want to run a function inside one of these only when it's the active slide. I tried below code, but once the state changes the entire ReactFullpage is re-rendered causing the first slide to be active again so I'm basically stuck in a loop.
My question is, how can I trigger a function inside the <Player /> component to run only if it's the active slide?
import React from "react";
import ReactFullpage from "#fullpage/react-fullpage";
import AlbumInfo from './AlbumInfo';
import Player from './Player';
class Album extends React.Component {
constructor(props){
super(props);
this.state={
playing: false
}
}
_initPlayer = (currentIndex, nextIndex) => {
if(nextIndex.index === 1) {
this.setState({playing:true})
}
}
render() {
return (
<ReactFullpage
licenseKey='xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx'
sectionsColor={["#000000"]}
afterLoad={this._initPlayer}
render={({ state, fullpageApi }) => {
return (
<div id="fullpage-wrapper">
<div className="section">
<AlbumInfo />
</div>
<div className="section">
<Player playing={this.state.playing} />
</div>
</div>
);
}}
/>
);
}
}
export default Album;
From docs:
just add the class 'active' to the section and slide you want to load first.
adding conditionally (f.e. using getActiveSection()) 'active' class name should resolve rerendering problem.
The same method/value can be used for setting playing prop.
Probably (I don't know/didn't used fullpage.js) you can also use callbacks (without state management and unnecessary render), f.e. afterSlideLoad
Update
The issue has been fixed on https://github.com/alvarotrigo/react-fullpage/issues/118.
Version 0.1.15 will have it fixed
You should be using fullPage.js callbacks afterLoad or onLeave as can be seen in the codesandbox provided on the react-fullpage docs:
https://codesandbox.io/s/m34yq5q0qx
/* eslint-disable import/no-extraneous-dependencies */
import React from "react";
import ReactDOM from "react-dom";
import "fullpage.js/vendors/scrolloverflow"; // Optional. When using scrollOverflow:true
import ReactFullpage from "#fullpage/react-fullpage";
import "./styles.css";
class FullpageWrapper extends React.Component {
onLeave(origin, destination, direction) {
console.log("Leaving section " + origin.index);
}
afterLoad(origin, destination, direction) {
console.log("After load: " + destination.index);
}
render() {
return (
<ReactFullpage
anchors={["firstPage", "secondPage", "thirdPage"]}
sectionsColor={["#282c34", "#ff5f45", "#0798ec"]}
scrollOverflow={true}
onLeave={this.onLeave.bind(this)}
afterLoad={this.afterLoad.bind(this)}
render={({ state, fullpageApi }) => {
return (
<div id="fullpage-wrapper">
<div className="section section1">
<h3>Section 1</h3>
<button onClick={() => fullpageApi.moveSectionDown()}>
Move down
</button>
</div>
<div className="section">
<div className="slide">
<h3>Slide 2.1</h3>
</div>
<div className="slide">
<h3>Slide 2.2</h3>
</div>
<div className="slide">
<h3>Slide 2.3</h3>
</div>
</div>
<div className="section">
<h3>Section 3</h3>
</div>
</div>
);
}}
/>
);
}
}
ReactDOM.render(<FullpageWrapper />, document.getElementById("react-root"));
export default FullpageWrapper;

TypeError: Cannot read property 'array' of undefined when trying to use google-maps-react

I am new to ReactJS, but familiar with React Native. I am trying to to get a basic map component to be displayed.
My map component is
WaterMapView.js
import React, { Component } from 'react';
import { Map, InfoWindow, Marker, GoogleApiWrapper } from 'google-maps-react';
export class WaterMapView extends Component {
render () {
console.log(' I am here ');
return (
<Map
google={this.props.google}
style={styles.mapStyle}
initialCenter={{
lat: 40.854885,
lng: -88.081807
}}
zoom={15}
/>
)
}
}
const styles = {
mapStyle: {
height: '100%',
width: '100%'
}
}
export default GoogleApiWrapper({
apiKey: 'AIxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
})(WaterMapView)
This component is embedded in MainDashboard.js as follows :
import React, { Component } from 'react';
import windowSize from 'react-window-size';
import WaterMapView from './WaterMapView';
class MainDashboard extends Component {
render () {
const height = this.props.windowHeight;
const {
containerStyle,
headerStyle,
navigationStyle,
mainPageStyle,
eventPageStyle,
mapPageStyle,
mapDisplayStyle,
mapPropertiesStyle
} = styles;
return (
<div style={{ ...containerStyle, height }} >
<div style={headerStyle} >
</div>
<div style={navigationStyle} >
</div>
<div style={mainPageStyle} >
<div style={eventPageStyle} >
</div>
<div style={mapPageStyle} >
<div style={mapDisplayStyle} >
<WaterMapView />
</div>
<div style={mapPropertiesStyle} >
</div>
</div>
</div>
</div>
);
};
};
My App.js component is :
import React from 'react';
import MainDashboard from './MainDashboard';
const App = () => {
return (
<div>
<MainDashboard />
</div>
)
};
export default App
And index.js is :
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './Components/App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
I am using React version 16.0.0 and google-maps-react version 1.1.0
I get an error accompanied by a dump of code. The error is :
TypeError: Cannot read property 'array' of undefined
I am not including the dump in this post. I can add that if required.
The problem seems very basic as the I do not even see the console.log statement 'I am here' in WaterMapView.js component.
Let me know what am I missing.
<div style={mapDisplayStyle} >
WaterMapView
</div>
Should be
<div style={mapDisplayStyle} >
<WaterMapView />
</div>
Otherwise react just interprets WaterMapView as a string and displays the string. When you want to use/render the component you need to add < > to id.

How to perform jQuery slideToggle() equivalent in reactjs?

The basic idea is to produce jQuery's slideToggle() animation in reactjs.
Hiding an element and showing it based on its state is fairly straightforward, but actually animating the height, so it looks like it's sliding up and down, seems to be more complex than I thought in reactjs. I've googled around for this type of animation and cannot find anything.
The closest I've found is people saying use the "max-height" css property and animate with that, however, that requires you to set a max-height on all divs you want to animate. And with responsive content this is just not the right way to go. On one screen the max height needed might be 200, but on mobile maybe 500!
Here is where I am so far, I can easily collapse/expand a component with the state like I said, but how do I expand this to actually animate? And handle mid animation clicks, so it goes back when needed?
The height-0 css class is just this:
.height-0 {
overflow: hidden;
max-height: 0;
}
import React, { Component, PropTypes } from 'react';
export default class CollapsableComponent extends Component {
constructor(props) {
super(props);
this.state = {
collapsed: false
};
}
toggleCollapse(){
this.setState({
...this.state,
collapsed: this.state.collapsed ? false : true;
});
}
render() {
return (
<div class="row">
<div class="col-sm-12">
<h2>Some Title....
<button class="btn btn-default pull-right" onClick={this.toggleCollapse}>
<span class={`fa fa-${collapsed ? 'expand' : 'compress'}`} aria-hidden="true"/>
</button>
</h2>
<div class={`animation-holder${collapsed ? ' height-0' : ''}`} ref={(div) => { this.holderDiv = div;}}>
<p>content here......</p>
</div>
</div>
</div>
);
}
}
The simplest way I can think of is the following: Sandbox
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [isOpen, setIsOpen] = useState(false);
const style = {
overflow: "hidden",
height: isOpen ? 50 : 0,
transition: "2s"
};
return (
<div className="App">
<div style={style}>
<p> Let me slide in and out!</p>
</div>
<button onClick={() => setIsOpen(prev => !prev)}>Slide Toggle</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
If the want the actual height of the component you could retrieve it with the use of the useRef hook like this: ref.current.clientHeight.

Resources