When I click on the element AppBar, icon on the left, _handleClick() method should execute.
I can't get console message.
I'm using material-ui framework and the attribute onLeftIconButtonTouchTap is provided for a callback function for when the left icon is selected via a touch tap.
import React, { Component } from 'react'
import { AppBar, IconButton } from 'material-ui'
import MoreVertIcon from 'material-ui/lib/svg-icons/navigation/more-vert';
let injectTapEventPlugin = require("react-tap-event-plugin");
//Needed for onTouchTap
//Can go away when react 1.0 release
//Check this repo:
//https://github.com/zilverline/react-tap-event-plugin
injectTapEventPlugin();
class Header extends Component {
constructor(props) {
super(props);
this._handleClick = this._handleClick.bind(this);
}
_handleClick(e) {
e.preventDefault();
// Show/Hide the LeftMenu
window.console.log("Click!");
}
render() {
return (
<AppBar title="Arasaaccc"
iconElementLeft={ <IconButton>
<MoreVertIcon/>
</IconButton> }
onLeftIconButtonTouchTap={ this._handleClick }
isInitiallyOpen={ true } />
)
}
}
export default Header
However it works with another component:
class Prueba extends Component {
constructor(props) {
super(props);
this._handleClick = this._handleClick.bind(this);
}
_handleClick(e) {
e.preventDefault();
window.console.log("Click!");
}
render (){
return (
<h1 onClick={this._handleClick}>Prueba Prueba Prueba</h1>
)
}
}
export default Prueba;
If you specify an icon for the AppBar component, onLeftIconButtonTouchTap event does not work.
Either you don't specify an icon:
<AppBar title="Arasaaccc"
onLeftIconButtonTouchTap={ this._handleClick }
isInitiallyOpen={ true } />
Or you apply the event on the IconButton component:
<AppBar title="Arasaaccc"
iconElementLeft={ <IconButton onTouchTap={ this._handleClick } >
<MoreVertIcon />
</IconButton> }
isInitiallyOpen={ true } />
Edit: Note that, according to this GitHub issue, the problem should be solved. You still can't have a a _handleClick on both of iconElementLeft and onLeftIconButtonTouchTap, either one or the other.
I can't see any problems with your code, so my guess is you'll need the React-Tap-Event-Plugin. The docs say this dependency is temporary and will go away once react v1.0 is released.
Related
I have a problem with a fullscreen dialog that is being closed when the associated "OnClose" function is called. The dialog closes, however i cannot be opened again.
Any idea on why this happens? It feels like there is an invisible dialog staying on the canvans that prevents event from being bubbled to the button, or something similar.
import React from "react";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import "./FooterBar.css";
import Slide from "#material-ui/core/Slide";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import IconButton from "#material-ui/core/IconButton";
import CloseIcon from "#material-ui/icons/Close";
class BarItem extends React.Component {
constructor(props) {
super(props);
this.state = {
title: props.title,
targetURL: props.targetURL,
dialogOpen: false
};
this.barItemClicked = this.barItemClicked.bind(this);
this.handleClose = this.handleClose.bind(this);
}
barItemClicked() {
this.setState({
dialogOpen: true
});
}
handleClose() {
this.setState({
dialogOpen: false
});
}
render(props) {
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
return (
<div>
<Button onClick={this.barItemClicked}>{this.state.title}</Button>
<Dialog
fullScreen
open={this.state.dialogOpen}
onClose={this.handleClose}
TransitionComponent={Transition}
>
<AppBar>
<Toolbar>
<IconButton
edge="start"
color="inherit"
onClick={this.handleClose}
aria-label="Close"
>
<CloseIcon />
</IconButton>
</Toolbar>
</AppBar>
</Dialog>
</div>
);
}
}
class FooterBar extends React.Component {
render() {
return (
<div class="footerbar">
<BarItem title="Impressum" targetURL="a" />
<BarItem title="Datenschutzerklärung" targetURL="b" />
<BarItem title="Kontakt" targetURL="c" />
</div>
);
}
}
export default FooterBar;
I expect the buttons of the Footerbar to re-open the dialog, but this does not happen.
It looks like the problem lies in your TransitionComponent, you're passing a new instance of it to your Dialog each time you render. Try declaring it outside of your BarItem class.
Also, depending on what you want to display in your modal, I would find it better to put the modal and handler in your FooterBar component. Take a look at this sandbox that I created from your code. Maybe it'll give you some ideas.
Let me know if it helps.
I am trying to make a click on a button of a child React component change the Boolean state of the child and of its parent.
The issue here is that it has to change states of both components.
Here is a link for the code I am trying to get working:
https://stackblitz.com/edit/child-to-parent-state-pass-bkmvwc?file=Child.js
The requirement is to click the hamburger button and it changes the state of the child component (the actual hamburger button) and its parent component.
Thank you!
I would not recommend doing what you are doing.
But, knowing nothing of your background I will only answer your question.
In parent.js you are missing the bind to this.
Use this line instead and check if this works
Why don't you manage the state from the parent component like:
Parent.js:
import React from 'react'
import { Link } from 'react-router-dom'
import { initializeIcons } from '#uifabric/icons';
import Hamburger from './Child'
initializeIcons();
export default class NavBar extends React.Component {
constructor(props) {
super(props);
this.state = {
opened: false
};
}
handleCounter = () => {
this.setState({ opened: !this.state.opened });
}
render() {
return (
<Hamburger
opened={this.state.opened}
handleCounter={this.handleCounter}
/>
);
}
}
Child.js
import React from 'react'
import { IconButton } from 'office-ui-fabric-react/lib/Button';
import { initializeIcons } from '#uifabric/icons';
initializeIcons();
export default class Hamburger extends React.Component {
constructor(props) {
super(props);
}
updateParent() {
this.props.handleCounter(this.state);
}
render() {
return (
<IconButton
checked={this.props.opened}
iconProps={{ iconName: (this.props.opened ? 'Cancel' : 'GlobalNavButton'), style: { fontSize: 35 } }}
className="hamburger mobile-only"
title="Open Global Navigation"
ariaLabel="Open Global Navigation"
styles={{
root: {
padding: '0',
border: 'none',
background: 'transparent !important'
}
}}
onClick={this.props.handleCounter}
/>
);
}
}
PS: I removed the comments for readability
You would only change the state of the parent. The child would just read the props that are passed to it.
Parent component
constructor(props){
super(props)
this.state = {
hamburgerOpen: false
}
}
handleHamburgerToggle = () => {
let { hamburgerOpen } = this.state;
this.setState({
hamburgerOpen: !hamburgerOpen
})
}
render() {
let { hamburgerOpen } = this.state;
return (
<Child
hamburgerOpen={hamburgerOpen}
handleHamburgerToggle={this.handleHamburgerToggle}
/>
)
}
Child will have access to the props passed to it. You can make the Hamburger a functional component as well since it isn't concerned about the current state, only the parent is.
hamburgerOpen and toggleHamburgerOpen
Child Component
const { handleHamburgerToggle } = this.props;
return {
<div>
<div
onClick={() => handleHamburgerToggle()}
>
Click me to toggle hamburger
</div>
</div>
}
The demo case is here. To regenerate it, just do as follows:
click on [Click me.], then the popup will show;
click anywhere but the popuped block, the popup will hide;
click on [Click me.], it's expected that the popup window will show again, but the fact is just the opposite.
What's the problem? Any ideas?
Use onClose prop of reactjs-popup as shown below.
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Popup from "reactjs-popup";
class App extends Component {
constructor() {
super(
);
this.onClick = this.onClick.bind(this);
this._popUpClosed = this._popUpClosed.bind(this);
this.state = {
name: 'React',
open:false
};
}
_popUpClosed(){
this.setState({open: false});
}
onClick() {
this.setState({open: true});
}
render() {
return (
<div>
<Popup open={this.state.open} onClose={this._popUpClosed}/>
<Hello name={this.state.name} />
<button onClick={this.onClick}>
Click me.
</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));
it seems like you need to make open false again so that the popup can show up when you change the state of open
you can change the onClick function like this
onClick() {
this.setState({open: !this.state.open});
}
like this you will need to double click in the second time. or better you can add a button in your pop up to close it
New to React. Just using create-react-app and Material UI, nothing else.
Coming from an Angular background.
I cannot communicate from a sibling component to open the sidebar.
I'm separating each part into their own files.
I can get the open button in the Header to talk to the parent App, but cannot get the parent App to communicate with the child LeftSidebar.
Header Component
import React, {Component} from 'react';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationMenu from 'material-ui/svg-icons/navigation/menu';
class Header extends Component {
openLeftBar = () => {
// calls parent method
this.props.onOpenLeftBar();
}
render() {
return (
<AppBar iconElementLeft={
<IconButton onClick={this.openLeftBar}>
<NavigationMenu />
</IconButton>
}
/>
);
}
}
export default Header;
App Component -- receives event from Header, but unsure how to pass dynamic 'watcher' down to LeftSidebar Component
import React, { Component } from 'react';
import darkBaseTheme from 'material-ui/styles/baseThemes/darkBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import RaisedButton from 'material-ui/RaisedButton';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
// components
import Header from './Header/Header';
import Body from './Body/Body';
import Footer from './Footer/Footer';
import LeftSidebar from './LeftSidebar/LeftSidebar';
class App extends Component {
constructor() {
super() // gives component context of this instead of parent this
this.state = {
leftBarOpen : false
}
}
notifyOpen = () => {
console.log('opened') // works
this.setState({leftBarOpen: true});
/*** need to pass down to child component and $watch somehow... ***/
}
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<Header onOpenLeftBar={this.notifyOpen} />
<Body />
<LeftSidebar listenForOpen={this.state.leftBarOpen} />
<Footer />
</div>
</MuiThemeProvider>
);
}
}
export default App;
LeftSidebar Component - cannot get it to listen to parent App component - Angular would use $scope.$watch or $onChanges
// LeftSidebar
import React, { Component } from 'react';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
class LeftNavBar extends Component {
/** unsure if necessary here **/
constructor(props, state) {
super(props, state)
this.state = {
leftBarOpen : this.props.leftBarOpen
}
}
/** closing functionality works **/
close = () => {
this.setState({leftBarOpen: false});
}
render() {
return (
<Drawer open={this.state.leftBarOpen}>
<IconButton onClick={this.close}>
<NavigationClose />
</IconButton>
<MenuItem>Menu Item</MenuItem>
<MenuItem>Menu Item 2</MenuItem>
</Drawer>
);
}
}
export default LeftSidebar;
Free your mind of concepts like "watchers". In React there is only state and props. When a component's state changes via this.setState(..) it will update all of its children in render.
Your code is suffering from a typical anti-pattern of duplicating state. If both the header and the sibling components want to access or update the same piece of state, then they belong in a common ancestor (App, in your case) and no where else.
(some stuff removed / renamed for brevity)
class App extends Component {
// don't need `constructor` can just apply initial state here
state = { leftBarOpen: false }
// probably want 'toggle', but for demo purposes, have two methods
open = () => {
this.setState({ leftBarOpen: true })
}
close = () => {
this.setState({ leftBarOpen: false })
}
render() {
return (
<div className="App">
<Header onOpenLeftBar={this.open} />
<LeftSidebar
closeLeftBar={this.close}
leftBarOpen={this.state.leftBarOpen}
/>
</div>
)
}
}
Now Header and LeftSidebar do not need to be classes at all, and simply react to props, and call prop functions.
const LeftSideBar = props => (
<Drawer open={props.leftBarOpen}>
<IconButton onClick={props.closeLeftBar}>
<NavigationClose />
</IconButton>
</Drawer>
)
Now anytime the state in App changes, no matter who initiated the change, your LeftSideBar will react appropriately since it only knows the most recent props
Once you set the leftBarOpen prop as internal state of LeftNavBar you can't modify it externally anymore as you only read the prop in the constructor which only run once when the component initialize it self.
You can use the componentWillReceiveProps life cycle method and update the state respectively when a new prop is received.
That being said, i don't think a Drawer should be responsible for being closed or opened, but should be responsible on how it looks or what it does when its closed or opened.
A drawer can't close or open it self, same as a light-Ball can't turn it self on or off but a switch / button can and should.
Here is a small example to illustrate my point:
const LightBall = ({ on }) => {
return (
<div>{`The light is ${on ? 'On' : 'Off'}`}</div>
);
}
const MySwitch = ({ onClick, on }) => {
return (
<button onClick={onClick}>{`Turn the light ${!on ? 'On' : 'Off'}`}</button>
)
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
lightOn: false
};
}
toggleLight = () => this.setState({ lightOn: !this.state.lightOn });
render() {
const { lightOn } = this.state;
return (
<div>
<MySwitch onClick={this.toggleLight} on={lightOn} />
<LightBall on={lightOn} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I want to create a custom react popover, based on Bootstrap Popover, just with my own design and maybe more props.
I created a new react component called MyTooltip :
import React, {Component} from 'react';
import { Popover } from 'react-bootstrap';
export default class MyPopover extends Component{
constructor(props){
super(props);
}
render() {
return (
<Popover id="popover-trigger-focus" title={this.props.title}>
{ return this.props.children }
</Popover>
);
}
}
and in my main component I tried to create a trigger:
export default class MyMainComponent extends Component{
render() {
const popoverFocus = (
<MyPopover id="tooltip-trigger-focus" title="My Title">
my text <b>my bold text</b> my text
</MyPopover >
);
return (
<div >
<OverlayTrigger trigger="focus" placement="bottom" overlay={popoverFocus}>
<Button>Focus</Button>
</OverlayTrigger>
</div>
);
}
}
But it doesn't work. The button does hide and show the popover with the right text, but the popover ignores the "placement" property and it doesn't have the fade animation.
Any help will be useful...
You've set the placement and overlay prop to the <OverlayTrigger/> element, but those belong on the Popover. You can quickly fix this by letting the popover inherit the props from the trigger element like so:
render() {
return (
<Popover id="popover-trigger-focus" title={this.props.title} {...this.props}>
{ this.props.children }
</Popover>
);
}
Here's your example with {...this.props}.
PS: You don't need to return JS in JSX (e.g. { return this.props.children }). You can just leave that out ({ this.props.children }).