reactstrap close Nav when link clicked - reactstrap

I'm sure this has been asked before however, I can't find the answer. How do you get the Nav menu to close when you click on a NavLink in reactstrap or is this still in development?

Reactstrap has an isOpen parameter in state, so you need to set it to false
constructor(props) {
super(props);
this.closeNavbar = this.closeNavbar.bind(this);
this.state = {
isOpen: false
};
}
closeNavbar() {
this.setState({
isOpen: false
});
}
// and in Link or a element add onClick handler like this
<Link to="/" onClick={closeNavbar}>Home</Link>
Here is the code I use:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import library from './FontAwesomeLibrary';
import logo from '../../assets/images/logo.svg';
import {
Collapse,
Navbar,
NavbarToggler,
Nav,
NavItem } from 'reactstrap';
class Topbar extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.closeNavbar = this.closeNavbar.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
this.state = {
isOpen: false,
};
}
componentWillMount() {
document.addEventListener('mousedown', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside);
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
closeNavbar() {
this.setState({
isOpen: false
});
}
handleClickOutside(event) {
const t = event.target;
if (this.state.isOpen && !t.classList.contains('navbar-toggler')) {
this.closeNavbar();
}
}
render() {
return (
<div className="topbar">
<section className="container">
<Navbar color="light" className="header" expand="md">
<Link className="locoLink" to="/"><img src={logo} className="logo" alt="logo" /></Link>
<Link to="/" className="logoCompany">Redux Blog</Link>
<NavbarToggler onClick={this.toggle}>
<FontAwesomeIcon icon={this.state.isOpen ? "times" : "bars"}/>
</NavbarToggler>
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto routes" navbar>
<NavItem>
<Link to="/" onClick={this.closeNavbar}>Posts</Link>
</NavItem>
<NavItem>
<Link to="/posts/new" onClick={this.closeNavbar}>New Post</Link>
</NavItem>
</Nav>
</Collapse>
</Navbar>
</section>
</div>
);
}
}
export default Topbar;
It also handles outside clicks.
FontAwesomeLibrary
import { library } from '#fortawesome/fontawesome-svg-core';
import { faCoffee } from '#fortawesome/free-solid-svg-icons';
import { faMugHot } from '#fortawesome/free-solid-svg-icons';
import { faTimes } from '#fortawesome/free-solid-svg-icons';
import { faBars } from '#fortawesome/free-solid-svg-icons';
import { faChevronUp } from '#fortawesome/free-solid-svg-icons';
library.add(faCoffee);
library.add(faMugHot);
library.add(faTimes);
library.add(faBars);
library.add(faChevronUp);
export default library;
I created FontAwesomeLibrary according to
https://fontawesome.com/how-to-use/on-the-web/using-with/react

This is an issue with routing. On a normal webpage a link in a bootstrap nav would cause a new page to load with the nav now closed. Since react doesn't reload the page the nav remains open. In order to fix this you'll need to add the following to each of your <Link> elements in the nav: onClick={this.toggleNavbar}.

Related

How can I create horizontal and vertical animation slider using react-stack-cards in ReactJs?

import React, { Component } from 'react'
import { ToggleCard, TinderLikeCard, StackCard } from 'react-stack-cards'
class Card extends React.Component {
constructor(props) {
super(props)
this.state = {
directionTinder: "swipeCornerDownRight",
directionToggle: "sideSlide",
directionStack: "topRight",
isOpen: false
}
this.Tinder = null
}
onTinderSwipe() {
this.Tinder.swipe()
}
onToggle() {
this.setState({ isOpen: !this.state.isOpen })
}
render() {
const {index, pic, name}=this.props.property
return (
<div className={`card-${index}`}>
<h1>{index}</h1>
<TinderLikeCard
images={pic}
width="550"
height="450"
direction={this.state.directionTinder}
duration={400}
ref={(node) => this.Tinder = node}
className="tinder"
>
</TinderLikeCard>
<button onClick={this.onTinderSwipe.bind(this)}>Swipe</button>
<h1>{name}</h1>
</div>
);
}
}
export default Card

React modal is not showing in the center of the screen

With the help of the community, I have been able to final have my modal pop-up working on my reat website.
The modal code is as below:
import React from "react";
import '../../assets/styles/GenericTheme.css'
import { Modal, Button } from "react-bootstrap";
class LoginRegisterModal extends React.Component {
constructor(props, context) {
super(props);
this.state = {show: false};
}
open = () => {
this.setState({show: true});
}
close = () => {
this.setState({show: false});
}
componentDidUpdate(prevProps) {
const { show } = this.props;
if (prevProps.show !== show) {
if (show) {
this.open(); // open if parent says to
} else {
this.close(); // close if parent says to
}
}
}
render() {
const styleModal = {
width:770,
height:480,
backgroundColor:"#ffffffff",
borderRadius:21.5,
boxShadow: "0px 8px 18px 0 rgba(0,0,0,0.14)",
}
return (
<Modal show={this.state.show} style={styleModal} >
<Modal.Body>test</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.close}>
Close
</Button>
</Modal.Footer>
</Modal>
);
}
}
export default LoginRegisterModal;
The modal is called by the react-bootstrap Navs components:
const menuLoginRegister = <Nav.Link ref="LoginRegisterModal" eventKey={1} href="#" onClick={this.openLogin}>{TextContents.MenuLoginRegister}</Nav.Link>;
And I have added the modal in the return function like below:
return (
<div>
<Navbar className="village-header" width="100" expand="lg">
....
</Navbar>
<LoginRegisterModal show={this.state.showLogin}
onHide={() => this.setState({ showLogin: false })}/>
</div>
);
The only issues is that the modal keep being displayed on the top left corner and not centered in the screen.
I tried to use a center in the style but it still not move it to the center.
Any idea how to do it ?
Thanks

Reactjs Media query application

I have a separate App.css file that has global css attributes and have classes for responsiveness. The issue is I want to render elements differently for separate devices but can't seem to figure out how to do that as using conditionals isn't applying as such.
import UserItem from "./UserItem";
import Spinner from "../layout/Spinner";
import PropTypes from "prop-types";
const Users = ({ users, loading }) => {
if (loading) {
return <Spinner />;
} else {
return (
<div style={userStyle} className='body'>
{users.map((user) => {
return <UserItem key={user.id} user={user} />;
})}
</div>
);
}
};
const windowWidth = window.innerWidth;
Users.propTypes = {
users: PropTypes.array.isRequired,
loading: PropTypes.bool.isRequired,
};
const userStyle = {
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gridGap: "1rem",
};
export default Users;
My css #media query which I am trying to apply to effect change on a small device.
/* Mobile Styles */
#media (max-width: 700px) {
.hide-sm {
display: none;
}
}
How do I implement this #media css style so that it can render the page differents through jsx?
You can use material ui. that will fulfil your requirement. Please check this example:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Typography from '#material-ui/core/Typography';
import { green } from '#material-ui/core/colors';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
backgroundColor: theme.palette.secondary.main,
},
[theme.breakpoints.up('md')]: {
backgroundColor: theme.palette.primary.main,
},
[theme.breakpoints.up('lg')]: {
backgroundColor: green[500],
},
},
}));
export default function MediaQuery() {
const classes = useStyles();
return (
<div className={classes.root}>
<Typography variant="subtitle1">{'down(sm): red'}</Typography>
<Typography variant="subtitle1">{'up(md): blue'}</Typography>
<Typography variant="subtitle1">{'up(lg): green'}</Typography>
</div>
);
}
Material UI
You can use following example too.
class Card extends Component {
constructor() {
super();
this.mediaQuery = {
desktop: 1200,
tablet: 768,
phone: 576,
};
this.state = {
windowWidth: null
};
}
componentDidMount() {
window.addEventListener('resize', () => {
this.setState({windowWidth: document.body.clientWidth})
});
}
render() {
return (
<div style={{
width: this.state.windowWidth > this.mediaQuery.phone
? '50%'
: '100%',
//more styling :)
}}>
<!-- <Card> contents -->
</div>
);
}
}
Source
I suggest that use CSS #media query to make responsive layouts.
But if you insist on implement with JS and React you should get windowWidth after component mounted. You can use useEffect hook to do so and save value in a state:
const [windowWidth, setWindowWidth] = useState('');
useEffect(() => {
setWindowWidth(window.innerWidth) // or better one -> window.clientWidth
});

React tutorial - css not loading

I'm working through a tutorial on React/Spring Boot located here. All was going well, including the initial display of groups on React.
However, once the React piece was refactored to separate the group list into a separate module and a nav bar was added, I get a display without any css rendering.
Here is the code:
App.js
import React, { Component } from 'react';
import './App.css';
import Home from './Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import GroupList from './GroupList';
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route path='/' exact={true} component={Home}/>
<Route path='/groups' exact={true} component={GroupList}/>
</Switch>
</Router>
)
}
}
export default App;
AppNavbar.js
import React, { Component } from 'react';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';
export default class AppNavbar extends Component {
constructor(props) {
super(props);
this.state = {isOpen: false};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
return <Navbar color="dark" dark expand="md">
<NavbarBrand tag={Link} to="/">Home</NavbarBrand>
<NavbarToggler onClick={this.toggle}/>
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto" navbar>
<NavItem>
<NavLink
href="https://twitter.com/oktadev">#oktadev</NavLink>
</NavItem>
<NavItem>
<NavLink href="https://github.com/oktadeveloper/okta-spring-boot-react-crud-example">GitHub</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>;
}
}
Home.js
import './App.css';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import { Button, Container } from 'reactstrap';
class Home extends Component {
render() {
return (
<div>
<AppNavbar/>
<Container fluid>
<Button color="link"><Link to="/groups">Manage JUG Tour</Link></Button>
</Container>
</div>
);
}
}
export default Home;
GroupList.js
import React, { Component } from 'react';
import { Button, ButtonGroup, Container, Table } from 'reactstrap';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
class GroupList extends Component {
constructor(props) {
super(props);
this.state = {groups: [], isLoading: true};
this.remove = this.remove.bind(this);
}
componentDidMount() {
this.setState({isLoading: true});
fetch('api/groups')
.then(response => response.json())
.then(data => this.setState({groups: data, isLoading: false}));
}
async remove(id) {
await fetch(`/api/group/${id}`, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then(() => {
let updatedGroups = [...this.state.groups].filter(i => i.id !== id);
this.setState({groups: updatedGroups});
});
}
render() {
const {groups, isLoading} = this.state;
if (isLoading) {
return <p>Loading...</p>;
}
const groupList = groups.map(group => {
const address = `${group.address || ''} ${group.city || ''} ${group.stateOrProvince || ''}`;
return <tr key={group.id}>
<td style={{whiteSpace: 'nowrap'}}>{group.name}</td>
<td>{address}</td>
<td>{group.events.map(event => {
return <div key={event.id}>{new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: '2-digit'
}).format(new Date(event.date))}: {event.title}</div>
})}</td>
<td>
<ButtonGroup>
<Button size="sm" color="primary" tag={Link} to={"/groups/" + group.id}>Edit</Button>
<Button size="sm" color="danger" onClick={() => this.remove(group.id)}>Delete</Button>
</ButtonGroup>
</td>
</tr>
});
return (
<div>
<AppNavbar/>
<Container fluid>
<div className="float-right">
<Button color="success" tag={Link} to="/groups/new">Add Group</Button>
</div>
<h3>My JUG Tour</h3>
<Table className="mt-4">
<thead>
<tr>
<th width="20%">Name</th>
<th width="20%">Location</th>
<th>Events</th>
<th width="10%">Actions</th>
</tr>
</thead>
<tbody>
{groupList}
</tbody>
</Table>
</Container>
</div>
);
}
}
export default GroupList;
App.css
.App {
text-align: center;
}
.container, .container-fluid {
margin-top: 20px;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
#media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
#keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Any help much appreciated.
If I understand correctly, you are not using the classes you define in the App.css file.
To use the styles add the className property to the element where you need the styling.
For example, in your home.js:
<Button className="App-link" color="link"><Link to="/groups">Manage JUG Tour</Link></Button>
As in the example App.js you linked:
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<div className="App-intro">
<h2>JUG List</h2>
{groups.map(group =>
<div key={group.id}>
{group.name}
</div>
)}
</div>
</header>
</div>
);
It was my fault, I just needed to add this into index.js, which was right in the instructions:
import 'bootstrap/dist/css/bootstrap.min.css';

How to retrieve plain CSS from inline style in ReactJS?

I'm using inline style to style the HTML DOM element. I want to display converted plain CSS. I'm changing the inline style using component state.
I do the following. It prints the style objects. e.g.,
{"display":"flex","flexDirection":"column"}
import React, {Component} from 'react';
class Sample extends Component {
constructor(props) {
super(props);
this.state = {
style: {
display: "flex",
flexDirection: "column"
},
}
}
render() {
const {style} = this.state;
return (
<div>
<div style={style}>
<div id={1}>1</div>
<div id={2}>2</div>
<div id={3}>3</div>
<div id={4}>4</div>
</div>
<div>{JSON.stringify(style)}</div>
</div>
);
}
}
export default Sample;
I expect the output as plain CSS instead of inline style object. e.g., "display: flex; flex-direction: column;"
This is some hack, but it will fulfil your requirement.
import React, {Component} from 'react';
class Sample extends Component {
constructor(props) {
super(props);
this.state = {
style: {
display: "flex",
flexDirection: "column"
},
}
}
getStyle(){
let styled = '{';
Object.keys(this.state.style).forEach(e => {
let key = e.split(/(?=[A-Z])/).join('-').toLowerCase()
styled += `${key}:${this.state.style[e]};`
});
styled += '}'
return styled;
}
render() {
const {style} = this.state;
return (
<div>
<div style={style}>
<div id={1}>1</div>
<div id={2}>2</div>
<div id={3}>3</div>
<div id={4}>4</div>
</div>
<div>{this.getStyle()}</div>
</div>
);
}
}
export default Sample;
Demo
Best way would be configure webpack to extract css to a new file.
npm install extract-text-webpack-plugin --save-dev
npm install style-loader css-loader --save-dev
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
]
}
I come across ReactJS ref document. And I tried the below way. It works as I was expecting. Demo
import React, { Component } from "react";
class Sample extends Component {
constructor(props) {
super(props);
this.itemContainerRef = React.createRef();
this.state = {
style: {
display: "flex",
flexDirection: "column"
},
itemContainerCSS: {}
};
}
componentDidMount() {
this.setState({
itemContainerCSS: this.itemContainerRef.current.style.cssText || {}
});
}
render() {
const { style, itemContainerCSS } = this.state;
return (
<div>
<div style={style} ref={this.itemContainerRef}>
<div id={1}>1</div>
<div id={2}>2</div>
<div id={3}>3</div>
<div id={4}>4</div>
</div>
<div>{JSON.stringify(itemContainerCSS)}</div>
</div>
);
}
}
export default Sample;

Resources