How to toggle elements with a button press in React? - javascript

I have searched for 3 freaking days to find out how to toggle my mobile nav button to toggle my mobile menu. I am new to React and could do this easily with jQuery but I don't want to use jQuery. I have line for line copied an example that I found on how to show or hide an element. I can not get it to work. Any help would be much appreciated. I am using styled-components with React.
Button sub-component:
import React, { Component } from 'react';
class MenuButton extends Component {
render() {
return (
<Button onClick={this.props.toggleMenu}>
<Menu></Menu>
</Button>
)
}
}
export default MenuButton;
Menu sub-component:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class Menu extends Component {
render() {
return (
<OffCanvasMenu>
<Title>Menu</Title>
<Nav>
<NavLinks><Link to='/'>Home</Link></NavLinks>
<NavLinks><Link to='/about'>About</Link></NavLinks>
<NavLinks><Link to='/interactive'>Interactive</Link></NavLinks>
<NavLinks><Link to='/ideas'>Ideas</Link></NavLinks>
<NavLinks><Link to='/contact'>Contact</Link></NavLinks>
</Nav>
</OffCanvasMenu>
)
}
}
export default Menu;
Menu Container component with all the state:
import React, { Component } from 'react';
import Menu from './Menu';
import MenuButton from './MenuButton';
class MenuContainer extends Component {
constructor(props) {
super(props);
this.state = {
active: false
}
this.toggleMenu = this.toggleMenu.bind(this);
}
toggleMenu() {
const { active } = this.state;
this.setState({
//toggle value of `active`
active: !active
});
}
render() {
return (
<div>
<MenuButton onClick={this.toggleMenu}/>
{this.state.active && <Menu/>}
</div>
)
}
}
export default MenuContainer;
I can see a checkbox in ReactDev tools that shows MenuContainer has state but when the button is clicked it does not toggle the state.

onClick is handled by MenuButton component which in turns invokes toggleMenu function passed as a property. I would pass toggleMenu as property of MenuButton:
<MenuButton toggleMenu={this.toggleMenu} />

Related

React Carousel: Previous and Next buttons aren't working

This is my index.js file
Imports
import React from 'react';
import ReactDOM from 'react-dom';
import field from './images/field.jpg';
import forest from './images/forest.jpg';
import hills from './images/hills.jpg';
import lake from './images/lake.jpg';
import sunrise from './images/sunrise.jpg';
Image component
class Image extends React.Component {
render() {
return (
<div className="img-container">
<img src={this.props.images[this.props.index]} alt={this.props.images[this.props.index]} />
</div>
);
}
}
This is reusable button component
class Button extends React.Component {
render() {
return (
<button className="button" >{this.props.content}</button>
);
}
}
This is two sliders that use button component
class Slider extends React.Component {
render() {
return (
<div className="slider">
<Button content={"❮"} onClick={this.props.decreaseIndex}/>
<Button content={"❯"} onClick={this.props.increaseIndex}/>
</div>
);
}
}
This is the main carousel component that encapsulates all other components
class Carousel extends React.Component {
constructor(props) {
super(props);
this.state = {
images: [field, forest, hills, lake, sunrise],
current: 0
}
}
changeIndex(what){
if (what > 0){
this.setState({
current: (this.state.current+1)%5
});
return;
}
this.setState({
current: (this.state.current-1)%5
});
}
increaseIndex(){
this.changeIndex(1);
}
decreaseIndex(){
this.changeIndex(-1);
}
render() {
return (
<div className="carousel">
<Image index={this.state.current} images={this.state.images}/>
<Slider clickPrev={this.decreaseIndex} clickNext={this.increaseIndex}/>
</div>
);
}
}
ReactDOM.render(<Carousel />, document.getElementById('root'));
I wanted to make a simple carousel using reactjs. It displays one image at a time, previous and next buttons change images. There are total 5 images to be displayed. On clicking 'next' or 'previous' button, images do not change as expected.
What is the exact mistake I am making here? I am new to react.
You have to bind your method changeIndex and the rest in your constructor. Like this:
constructor(props) {
super(props);
this.state = {
images: [field, forest, hills, lake, sunrise],
current: 0
}
this.changeindex = this.changeIndex.bind(this);
//other methods.
}
Read more in React Docs about events.
It is fixed,
Following changes were made:
Binding methods in constructor of Carousel class. Thanks to tksilicon's answer
In Slider class
<Button content={"❮"} work={this.props.decreaseIndex}/>
<Button content={"❯"} work={this.props.increaseIndex}/>
In Button
<button className="button" onClick={this.props.work}>{this.props.content}</button>

react router navigation image with text link not working properly

I got a simple vertical nav panel which nav sections consist of image to the left and some text to the right. It looks like the following:
The code of the nav section is as follows:
import React from 'react';
import { connect } from 'react-redux';
import data from '../../data/data.json'
export default class TopBarMenuItems extends React.Component {
constructor(props) {
super(props);
}
render() {
const MENUITEMS = data.menuData.map((item) => { //pls note use of loop here
let srcPath = require(`../../image/topBarMenuIcons/${item.src}`);
return (
<div className="menuItem" dataset={item.id}>
<a href={item.url} className="topbarLink">
<div>
<img src={srcPath} />
</div>
{item.name}
</a>
</div>
)
});
return MENUITEMS;
}
};
The problem is that when I click on the text (some link 1) I get redirected to the desired page, but when I click on the image, I dont. So the ideas is to get redirected to the same page when either image or text is clicked. Any ideas how to fix it would be welcome?
Thank you.
You can use react-router-dom library
import React from 'react';
import { connect } from 'react-redux';
import data from '../../data/data.json'
import { Link } from 'react-router-dom'
export default class TopBarMenuItems extends React.Component {
constructor(props) {
super(props);
}
render() {
const MENUITEMS = data.menuData.map((item) => {
let srcPath = require(`../../image/topBarMenuIcons/${item.src}`);
return (
<div className="menuItem" dataset={item.id}>
<Link to={item.url} className="topbarLink">
<div>
<img src={srcPath} />
</div>
{item.name}
</Link>
</div>
)
});
return MENUITEMS;
}
};

Display a new component with button click

Im making my first react ptoject. Im new in JS, HTML, CSS and even web app programing.
What i try to do, is to display some infomration on button click.
I have an API, that looks like this:
endpoint: https://localhost:44344/api/Projects
My Data from it:
[{"id":1,"name":"Mini Jira","description":"Description for first project in list","tasks":null},{"id":2,"name":"Farm","description":"Description for second one","tasks":null}]
And im fine with that, i can get it easily by axios in my react app.
Now i will show you my Project.js Component:
import React, { Component } from "react";
import { ListGroupItem, Button, ButtonToolbar } from "react-bootstrap";
import ProjectDetails from "./ProjectDetails";
class Project extends Component {
render() {
return (
<ButtonToolbar>
<ListGroupItem>{this.props.project.name}</ListGroupItem>
<Button onClick={Here i want to display new component with details }bsStyle="primary">Details</Button>
</ButtonToolbar>
);
}
}
export default Project;
I have all data from api in project type.
My question is, how to display component that i named ProjectDetails.js on button click? I want to show all data stored in project from my api in separate view (new page or somethig like that).
View looks like this:
Thanks for any advices!
EDIT:
based on #Axnyff answer, i edited Project.js. it works ok. But when i want to (for testing) displat project.name, i get error map of undefined. My ProjectDetails.js:
import React, { Component } from "react";
class ProjectDetails extends Component {
state = {};
render() {
return <li>{this.props.project.name}</li>;
}
}
export default ProjectDetails;
EDIT2:
In Project.js in #Axnyff answet i just edited that line:
{this.state.showDetails && (
<ProjectDetails project={this.props.project} />
)}
i passed project by props, now it works like i want too. After click it displays project.name that i clicked on.
You should use state in your React component.
Let's create a field called showDetails in your state.
You can initialize it in your constructor with
constructor(props) {
super(props); // needed in javascript constructors
this.state = {
showDetails: false,
};
}
Then you need to modify the onClick to set that state to true
<Button onClick={() => this.setState({ showDetails : true })} bsStyle="primary">Details</Button>
And then use that state to show or not the ProjectDetails:
{ showDetails && <ProjectDetails /> }
The full component should look like
import React, { Component } from "react";
import { ListGroupItem, Button, ButtonToolbar } from "react-bootstrap";
import ProjectDetails from "./ProjectDetails";
class Project extends Component {
constructor(props) {
super(props); // needed in javascript constructors
this.state = {
showDetails: false,
};
}
render() {
return (
<ButtonToolbar>
<ListGroupItem>{this.props.project.name}</ListGroupItem>
<Button onClick={() => this.setState({ showDetails : true })} bsStyle="primary">Details</Button>
{ this.state.showDetails && <ProjectDetails /> }
</ButtonToolbar>
);
}
}
export default Project;
You can then modify the logic to add a toggling effect etc.
If you haven't done it, you should probably follow the official tutorial
function Bar() {
return <h1>I will be shown on click!</h1>;
}
class Foo extends React.Component {
constructor() {
super();
this.state = { showComponent: false };
}
handleClick = () => {
this.setState({ showComponent: !this.state.showComponent });
};
render() {
return (
<div>
{this.state.showComponent && <Bar />}
<button onClick={this.handleClick}>click</button>
</div>
);
}
}
ReactDOM.render(<Foo />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

ReactJS: Separating Components Best Practices

So, I have a react-bootstrap nav and I want to have one of the nav items open and close a bootstrap modal component.
I have this working with this:
import React, { Component, render } from 'react';
import { Navbar, Nav, NavItem, NavDropdown, MenuItem, Modal, Button } from 'react-bootstrap';
export default class NavigationBar extends Component {
constructor() {
super();
this.state = {
showModal: false
}
}
close() { this.setState({ showModal: false }); }
open() { this.setState({ showModal: true }); }
render() {
return (
<div>
<Navbar>
...entire navbar...
</Navbar>
<Modal show={this.state.showModal} onHide={() => this.close()}>
...entire modal... which would be nice to put if a different file
</Modal>
</div>
); } }
Ideally, I would like to put the modal in a different component file and import it in, but when I do, I'm lost on how to translate the navbar open and close.
What is the best practice for combining components while maintaining their state across files?
Thanks!
A good way to think about it is containers vs presentational components. Containers hold your state and most of your logic. Presentational components take in inputs (props) and render html (jsx) [and do little else].
So, you could make your own Modal component that takes in the methods to call on close and one on whether or not it's shown. It could even be a stateless component - if it's just props + jsx, no need for a full class structure:
import React, { PropTypes } from 'react';
const MyModal = ({ show, onHide}) => (
<Modal show={show} onHide={onHide}>
// ...entire modal...
</Modal>
);
// displayName and propTypes are always good to have
MyModal.displayName = 'MyModal';
MyModal.propTypes = {
show: PropTypes.bool.isRequired,
onHide: PropTypes.func.isRequired,
};
export default MyModal;
then to use it, you will need to make sure to bind your methods so they're called in the right context:
class NavigationBar extends Component {
constructor() {
super();
this.state = {
showModal: false
};
// this is the important binding
this.close = this.close.bind(this);
this.open = this.open.bind(this);
}
close() { this.setState({ showModal: false }); }
open() { this.setState({ showModal: true }); }
render() {
return (
<div>
<Navbar>
// ...entire navbar...
</Navbar>
<MyModal
show={this.state.showModal}
onHide={this.close}
>
// child content if needed (unless it's all defined in MyModal)
</Modal>
</div>
);
}
}
You can wrap your react-bootstrap Modal with your content into your own custom component like so:
import React from 'react';
import { Modal } from 'react-bootstrap';
const NewModal = ({show, onHide}) => {
<Modal show={show} onHide={onHide}>
Modal content in here
</Modal>
};
export default NewModal;
And then import that modal from your component file
import Modal from 'components/modal' // Import your new modal's default export

How to override className on extended component?

I am trying to position this component differently on a certain page. But when I provide it with another className property it is only using the original class's styling that was provided when declaring the component.
Component:
import React, { Component } from 'react';
import styles from './label.css';
class Label extends Component {
render() {
return (
<div className={styles.labelClass} />
);
}
}
export default Label;
Page where I want to position it differently:
import React, { Component } from 'react';
import styles from './page.css';
import Label from '../common/label.jsx';
class Page extends Component {
render() {
return (
<div>
<Label className={styles.positionLeft} />
</div>
);
}
}
export default Page;
Normally I would do this with custom styling but I have to use media
queries so this isn't possible in this situation.
Since <Label> is a custom component, you can to manually pass the className prop down.
This is a good use case for default props!
class Label extends Component {
render() {
return (
<div className={this.props.className} />
);
}
}
Label.defaultProps = {
className: styles.labelClass
}
That way, if no className is provided to Label, it will use the labelClass style, otherwise, it will use the prop.
I fixed it by adding another optional property customClass to the component.
Label
import React, { Component } from 'react';
import styles from './label.css';
class Label extends Component {
render() {
return (
<div className={styles.labelClass + ' ' + this.props.customClass} />
);
}
}
export default Label;
Page
import React, { Component } from 'react';
import styles from './page.css';
import Label from '../common/label.jsx';
class Page extends Component {
render() {
return (
<div>
<Label customClass={styles.positionLeft} />
</div>
);
}
}
export default Page;
You need to explicitly reference the className property from Label's props - try:
import React, { Component } from 'react';
import styles from './label.css';
class Label extends Component {
render() {
let { className } = this.props
if (!className) {
className = styles.labelClass
}
return (
<div className={className} />
);
}
}
export default Label;

Categories

Resources