Open Drawer component from Toolbar component in React - reactjs

I have written a Nav component which renders react-md Drawer component. Two functions in this file control Drawer's visibility-
showDrawer = () => {
this.setState({ visible: true });
};
hideDrawer = () => {
this.setState({ visible: false });
};
This component is rendered at App level.
I have a page with Toolbar component. This toolbar has a menu button that should open the Drawer in Nav component.
class Catalog extends React.Component {
render() {
return (
<div>
<Toolbar themed fixed title="Catalog" nav={<Button icon onClick={this.showDrawer}>menu</Button>} />
....
How can I implement it in React?

You're close. Assuming we're just using plain-old React, you'll want to call the constructor function (which requires calling super as well), bind the 'this' context to the functions, define those functions and call them in the toolbar code you have below.
Assuming we're not involving Redux and using component state, you'll also want to set an initial state for your app (going with false for my example).
When I do this myself, I'll usually condense the functions into one 'toggle' function which you can do nicely with a ternary, checking the state to decide whether to open or close the nav.
Not sure if that fully answers your question, but hopefully it's a start.
All together, that looks something like this. (Note that the current implementation will only open the drawer and not close it):
export default class Catalog extends React.Component {
constructor(props) {
super(props);
this.hideDrawer = this.hideDrawer.bind(this);
this.showDrawer = this.showDrawer.bind(this);
this.getNavStyle = this.getNavStyle.bind(this);
this.state = { visible: false }
}
getNavStyle() {
return this.state.visible
? {
overflow: 'scroll',
width: '200px',
}
: {
overflow: 'visible',
letterSpacing: '2px',
width: '200px',
};
}
showDrawer = () => {
this.setState({ visible: true });
};
hideDrawer = () => {
this.setState({ visible: false });
};
render() {
return (
<div>
<Toolbar themed fixed title="Catalog"
nav={
<Button icon onClick={this.showDrawer}>
menu
</Button>}
/>
</div>
)
}
}

Related

How to pass an onMouseEnter function into child component

I would like to update my parent component's state when hovering over a child component. Basically, this parent component consists of an h1 and four components called Box. Each component has a title prop, which the component renders internally. What I would like to have happen is when a user hovers over a Box, the hovered state of the parent component changes to the title of the hovered child. Here is essentially what I have right now:
class Home extends React.Component {
constructor(props) {
super(props)
this.state = {
hovered: 'none'
};
this.handleHover = this.handleHover.bind(this);
}
handleHover = (n) => {
this.setState((n) => ({
hovered: n
}));
};
handleHoverOut = () => {
this.setState(() => ({
hovered: 'none'
}));
};
render() {
return (
<h1 className={this.state.hovered} >TITLE TITLE TITLE</h1>
<Box oME={ this.handleHover } oML={ this.handleHoverOut } title='Title1'/>
<Box oME={ this.handleHover } oML={ this.handleHoverOut } title='Title2'/>
<Box oME={ this.handleHover } oML={ this.handleHoverOut } title='Title3'/>
<Box oME={ this.handleHover } oML={ this.handleHoverOut } title='Title4'/>
)
}
class Box extends React.Component {
render() {
return(
<section onMouseEnter={() => { this.props.oME(this.props.title)}} onMouseLeave={() => { this.props.oML()}}>
...
</section>
}
I know this might not be 100% the way to go about it, but I think I'm somewhat on the right track! Please help me try to improve my code here, since I'm still learning the basics of React!
I created codesendbox where you can check the solution.
There were couple of issues in your code that I fixed there. Your state was not being displayed properly as title and there were unneeded callback functions.

why is react-player playing video even when playing={false}?

The react-player is playing video even when playing={false}. I'm using antd modal which is wrapped around my player.
when I close the modal state changes to false. Which is passed in react-player but the player keeps on playing in the background.
import React, { Component } from 'react'
import { Modal } from "antd";
import ReactPlayer from "react-player";
export class demo extends Component {
constructor(props) {
super(props);
this.state = {
isModalOpen: false,
};
}
ConfirmationModalhandleCancel = () => {
this.setState({ isModalOpen: false });
};
ConfirmationModalPlay = () => {
this.setState({ isModalOpen: true });
};
getVideo = () => {
console.log("Modal state",this.state.isModalOpen)
return (
<ReactPlayer
playing={this.state.isModalOpen}
className="introductionVideoLearningPlanPage"
controls={true}
url="https://www.youtube.com/watch?v=agi4geKb8v8"
/>
);
};
getIntroVideoModal = () => {
return (
<Modal
title={null}
footer={null}
visible={this.state.isModalOpen}
onCancel={this.ConfirmationModalhandleCancel}
width="fit-content"
bodyStyle={{ padding: '0' }}
>
{this.getVideo()}
</Modal>
)
}
render() {
return (
<div>
{this.getIntroVideoModal()}
</div>
)
}
}
export default demo
I have observed that the console.log("Modal state", this.state.isModalOpen) is consoling false when the modal closes but the player keeps on playing. react-player version: 2.6.2 and 2.9.0.
How do I stop playing the video once the modal closes?
Friend, you can go with Antd Modal itself, nothing wrong with that. To be able to stop the video playback when the Modal gets closed, you must pass the property
// add this property to your ant Modal
destroyOnClose={true}
to the Modal. This is an another workaround with which I achieved the expected behavior. Thanks
This issue is specific to antd modal. I tried reactstrap modal and it worked.

How to modify an existing div element in html by react?

Is it possible to use react to show and hide an existing div element by id?
For example, I want use react to have this <div id="example">this is example</div> show and hide by button click and this element is not created by react.
First you need to understand React's philosophy. I strongly suggest you read the official React tutorial: https://reactjs.org/tutorial/tutorial.html.
In this case you would like to appear and disappear a div, this can be accomplished by changing internal state of a React Component. Then you need to create a function that is bound to the context of the instance of the Component.
For example:
class Example extends Component {
constructor(props) {
super(props);
this.state = {
open: true,
};
}
toggle() {
const { open } = this.state;
this.setState({
open: !open,
});
}
render() {
const { open } = this.state;
return (
<main>
<button onClick={this.toggle.bind(this)}>{open ? 'Hide' : 'Show'}</button>
{open && <div id="example"><h1>this is example</h1></div>}
</main>
);
}
}
Here's a codepen: https://codepen.io/anon/pen/PxWdZK?editors=1010

Is there a way to make typescript generics props not read only ?

I am newbie in React and Typescript.In AlertDismissable class I am setting property of show when a request completed.I have used this sample and changed it a bit.
Depending on response I change AlertDismissable's contents and style.
When a user clicks hide button I am trying to set its show property to false.I have bound states with properties that's why I am trying to set props.However,compiler throws
TS2540: Cannot assign to 'show' because it is a constant or a read-only property.(handleDismiss method)
It seems generic props are read-only by default.Is there any other way to make it work ?
Here is my AlertDismissable tsx
import * as React from 'react'
import { Alert, Button } from 'react-bootstrap';
interface AlertDismissableState {
show: boolean;
style: string;
}
export default class AlertDismissable extends React.Component<AlertDismissableState, AlertDismissableState> {
constructor(props: any, context: any) {
super(props, context);
this.handleDismiss = this.handleDismiss.bind(this);
this.handleShow = this.handleShow.bind(this);
this.state = {
show: this.props.show,
style: this.props.style
};
}
handleDismiss() {
this.props.show=false;
}
handleShow() {
this.props.show=true;
}
render() {
if (this.props.show && this.props.style == "Success") {
return (
<Alert bsStyle="success">
<p>
Ok
</p>
<Button onClick={this.handleDismiss}>Hide Alert</Button>
</Alert>
);
}
if (this.props.show && this.props.style == "Danger") {
return (
<Alert bsStyle="danger">
<p>
Failed
</p>
<Button onClick={this.handleDismiss}>Hide Alert</Button>
</Alert>
);
}
return (<div />);
}
}
Here is my component that includes AlertDismissable class.I removed some codes for brevitiy.
export default class UploadContainer extends React.Component<{}, UploadContainerState> {
uploadFile(event: any) {
fetch("ProposalData/UploadFile", {
...
})
.then(handleErrors)
.then(function (response) {
that.setState({ alertVisible: true, style: "Success" });
}).catch(function (error) {
that.setState({ alertVisible: true, style: "Danger" }); });
}
render() {
return (
<div>
<AlertDismissable show={this.state.alertVisible} style={this.state.style} />
</div>)}
typescript:2.9.1
react: "^16.4.0"
react-bootstrap: "^0.32.1"
react-dom: "^16.4.0",
You are asking the wrong question. I will be answering a different one but the answer is the following:
In React you should never try to change props. Properties are what is being passed from the parent component. If you want to change properties, you have to go to the parent component and change what is being passed to AlertDismissable.
Actually, you should pass a property onDismiss of type function to AlertDismissable and call this.props.onDismiss() instead of this.props.show = false. Then you need to change the state of UploadContainer in that function.
Also note that your AlertDismissable class does not need to maintain state at all and it should use props directly.
I am not a typescript developer myself but it should be something like this:
interface AlertDismissableState {
show: boolean;
style: string;
onDismiss: () => void;
}
and then just:
<Button onClick={this.props.onDismiss}>
Hide Alert
</Button>
and in the parent component:
<AlertDismissable
show={this.state.alertVisible}
style={this.state.style}
onDismiss={() => this.setState({ alertVisible: false })}
/>
By the way, it does not make much sense to render AlertDismissable component when it should be hidden. You could consider:
{this.state.alertVisible && (
<AlertDismissable
style={this.state.style}
onDismiss={() => this.setState({ alertVisible: false })}
/>
)}
instead of passing the flag and render an empty <div/>.

Semantic-ui-react: How do I trigger a Popup without it being click/hover?

When submitting a form, I wish to show a small popup for 2.5 seconds if the server sends back a bad response.
The logic is fairly simple, however, I cannot figure out how to make this popup listen to a boolean somewhere in the state management(MobX in my case). I can get the content into the Popup just fine, however, the trigger is a button(and the content will show, if you click it) - But how do I make it listen to a boolean value somewhere?
Fairly simple class here:
import React from "react";
import { Popup, Button } from "semantic-ui-react";
import { inject } from "mobx-react";
const timeoutLength = 2500;
#inject("store")
export default class ErrorPopup extends React.Component {
state = {
isOpen: false
};
handleOpen = () => {
this.setState({
isOpen: true
});
this.timeout = setTimeout(() => {
this.setState({
isOpen: false
})
}, timeoutLength)
};
handleClose = () => {
this.setState({
isOpen: false
});
clearTimeout(this.timeout)
};
render () {
const errorContent = this.props.data;
if(errorContent){
return(
<Popup
trigger={<Button content='Open controlled popup' />}
content={errorContent}
on='click'
open={this.state.isOpen}
onClose={this.handleClose}
onOpen={this.handleOpen}
position='top center'
/>
)
}
}
}
However, the trigger value is a button which is rendered if this.props.data is present. But that's not the behavior I wish; I simply want the popup to render(and thus trigger) if this.props.data is there; alternatively, I can provide a true value with props if need be.
But how do I make this component trigger without it being a hover/button?
How about passing in the isOpen prop? Then you could add some logic onto the componentWillReceiveProps hook:
import React from "react";
import { Popup, Button } from "semantic-ui-react";
import { inject } from "mobx-react";
const timeoutLength = 2500;
#inject("store")
export default class ErrorPopup extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false,
}
};
//This is where you trigger your methods
componentWillReceiveProps(nextProps){
if(true === nextProps.isOpen){
this.handleOpen();
} else {
this.handleClose();
}
}
handleOpen = () => {
this.setState({
isOpen: true
});
this.timeout = setTimeout(() => {
//No need to repeat yourself - use the existing method here
this.handleClose();
}, timeoutLength)
};
handleClose = () => {
this.setState({
isOpen: false
});
clearTimeout(this.timeout)
};
render () {
const errorContent = this.props.data;
if(errorContent){
return(
<Popup
trigger={<Button content='Open controlled popup' />}
content={errorContent}
on='click'
open={this.state.isOpen}
position='top center'
/>
)
}
}
}
Without a need of handling delay - you could simply pass in the isOpen prop and that would do the trick.
And here what it could look like in your parent component's render:
let isOpen = this.state.isOpen;
<ErrorPopup isOpen={isOpen}/>
Set this value to control the popup, ideally, this should be a part of your parent component's state. Having a stateful component as a parent is important to make the popup re-rendered
Just use the property open as in the documentation. There is a separate example when popup is opened by default.

Resources