ReactStrap Collapse prevent toggle of siblings onClick - javascript

I researched this answer in couple of stackoverflow threads but nothing worked so far. I am using Reactstrap Collapse toggle, but instead of an outside button, I have the onClick method on an "li" tag.
The problem is that I am trying to stopPropagation to the additional "li's" without success. so if I click on one li, every li open, and closes.
import React, { Component } from 'react';
import { Col, Collapse, ListGroup, ListGroupItem } from 'reactstrap';
import Data from '../data/data.json';
class Example extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
collapse: false,
Data: Data
};
}
toggle = (e) => {
this.setState({ collapse: !this.state.collapse });
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}
render() {
return (
<Col xs={12} md={6} lg={6} className="right-col">
<ListGroup onClick={this.toggle}>
{ Data.projectLinks.map((link) => {
return <ListGroupItem key={link.title} onClick={(e) => {this.toggle(e)}}>
{link.title}
<Collapse isOpen={this.state.collapse}>
<p>{link.description}</p>
</Collapse>
</ListGroupItem>
})
}
</ListGroup>
</Col>
);
}
}
export default Example;
If you have any idea how to accomplish this, or another solution here, I appreciate it.

Related

The component is not hidden when the onClick functoin is called, but the state does change

I am learning React by reading the React wiki and following examples from freeCodeCamp, but I stumbled upon this issue and I can't seem to understand what I might be missing.
My intention is to have a panel with buttons, when you click on one of these a Component is shown or hidden.
import React, { Component, useState } from "react";
import { Box, Typography, Stack, Button, Divider } from '#mui/material';
import ListUsers from '../components/ListUsers'
import ListTasks from "../components/ListTasks";
class Admin extends Component {
constructor() {
super();
this.state = {
/*Start with all components hidden*/
toogleShowAllUsers: false,
toogleShowAllTasks: false
};
this.changeState = this.changeState.bind(this)
}
changeState(name) {
console.log(name)
switch(name) {
case 'toogleShowAllUsers':
this.state = ({ toogleShowAllUsers: !this.state.toogleShowAllUsers })
break;
case 'toogleShowAllTasks':
this.state = ({ toogleShowAllTasks: !this.state.toogleShowAllTasks })
break;
default:
}
}
render() {
const { toogleShowAllUsers, toogleShowAllTasks } = this.state
return (
/*Options menu*/
<Box>
<Typography
variant="h4"
fontWeight="bold"
sx={{ fontSize: { lg: '44px', xs: '30px' } }}
mb="46px">Admin Panel
</Typography>
<Stack
direction="row"
divider={<Divider
orientation="vertical"
flexItem
sx={{bgcolor: "secondary.light"}}/>}
spacing={2}>
<Button>Crear usuario</Button>
<Button>Eliminar usuario</Button>
<Button onClick={() => this.changeState('toogleShowAllUsers')}>Listar Usuarios</Button>
</Stack>
<Stack
direction="row"
divider={<Divider
orientation="vertical"
flexItem
sx={{bgcolor: "secondary.light"}}/>}
spacing={2}>
<Button>Crear tarea</Button>
<Button>Eliminar tarea</Button>
<Button>Asignar tarea</Button>
<Button onClick={() => this.changeState('toogleShowAllTasks')}>Listar Tareas</Button>
</Stack>
{/*Render options here*/}
{toogleShowAllUsers ? <ListUsers /> : null}
{toogleShowAllTasks ? <ListTasks /> : null}
</Box>
)
}
}
export default Admin;
I have inspected similar questions on SO here and here, but to no avail.
I would appreciate it, if you could assist/guide me on understanding what I might be missing or when did I do wrong in order to learn this awesome tool.
Quick fix, should be this.setState({ })
changeState(name) {
console.log(name)
switch(name) {
case 'toogleShowAllUsers':
// using functional state updater
this.setState((preState) => ({ toogleShowAllUsers: !preState.toogleShowAllUsers }))
break;
case 'toogleShowAllTasks':
this.setState((preState) => ({ toogleShowAllTasks: !preState.toogleShowAllTasks }))
break;
default:
}
}
More about using state in class components
The React beta docs are good starting point for functional components and managing state in function components
Hope it helps,

Add Class to the element where Clicked event happens in React JS

I have 5 such list items i.e self , parents , siblings , relative, friend. Clicking on any item , I am adding a class called active-option . Below is my code , what I have done so far. To note , I am a new to React JS.
import React, { Component } from 'react';
import {Grid, Col, Row, Button} from 'react-bootstrap';
import facebook_login_img from '../../assets/common/facebook-social-login.png';
const profilesCreatedBy = ['Self' , 'Parents' , 'Siblings' , 'Relative' , 'Friend'];
class Register extends Component {
constructor(props) {
super(props);
this.state = { addClass: false };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ addClass: !this.state.addClass });
}
render() {
let selectOption = ["option"];
if (this.state.addClass) {
selectOption.push("active-option");
}
return (
<section className="get-data__block" style={{padding: '80px 0 24px 0'}}>
<Grid>
<Row>
<Col sm={10} md={8} mdOffset={2} smOffset={1}>
<p className="grey-text small-text m-b-32"><i>
STEP 1 OF 6 </i>
</p>
<div className="data__block">
<div className="step-1">
<p className="m-b-32">This profile is being created by</p>
<Row>
{profilesCreatedBy.map((profileCreatedBy, index) => {
return <Col className="col-md-15">
<div onClick={this.handleClick} className={selectOption.join(" ")}>
{profileCreatedBy}
</div>
</Col>;
})}
</Row>
</div>
<Row className="text-center">
<Col xs={12} className="text-center">
<Button href="#" bsStyle="primary" className="m-t-96 m-b-16 has-box__shadow" >
Continue
</Button>
</Col>
</Row>
</div>
</Col>
</Row>
</Grid>
</section>
);
}
}
export default Register;
I am using a map function to display all items. I have tried to add a class called active-option to option. But clicking on any item is adding the class to every other item also. (Attached) Any suggestion ? I want to add active-option class to the one where click event happens, not to every other element. Siblings should not contain active-option class. Please help !
You can achieve this with keeping active item id in the state of component, for example:
class Test extends React.Component{
constructor(){
super();
this.state = {
activeId: null
}
this.setActiveElement = this.setActiveElement.bind(this);
}
setActiveElement(id){
this.setState({activeId: id})
}
render(){
return(
<div>
{
[1,2,3,4,5].map((el, index) =>
<div className={index === this.state.activeId? "active" : ""} onClick={() => this.setActiveElement(index)}>click me</div>
)
}
</div>
)
}
}
https://jsfiddle.net/69z2wepo/85095/

Native base list item click event not working

I have started learning react native 2 days back only. I am using list item component from Native Base UI framework.
According to their docs and examples to catch a click event on ListItem you need to add onPress and button option to the ListItem. But in my case its not working.
I have another element with also tracks click event, it works fine, but list element isn't catching click event.
Strange this is that if I trigger a alert, it works
<List button onPress={() => { Alert.alert('Item got clicked!') } }>
Below id my complete code
import React from 'react';
import {
Content,
List,
ListItem,
Body,
Thumbnail,
Text,
Badge,
View
} from 'native-base';
import { ActivityIndicator, TouchableHighlight, TouchableOpacity, Alert } from 'react-native';
export default class Questions extends React.Component{
constructor(props){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
Alert.alert("I am clicked");
// Call method from parent
this.props.onPress();
}
render() {
var items = this.props.items;
return (
<Content>
<List button onPress={() => { this.handleClick } }>
{Object.keys(items).map(function(eachQuestion) {
return (
<ListItem avatar key={items[eachQuestion].id} button onPress={() => { this.handleClick } } >
<Body>
<Text>{items[eachQuestion].question}</Text>
</Body>
</ListItem>
)
})}
</List>
<TouchableOpacity onPress={this.handleClick}>
<View><Text>Click me</Text></View>
</TouchableOpacity>
</Content>
);
}
}
Edit 1
render() {
var questions = {
"1" : "James",
"2" : "Smith",
"3" : "Doe",
"4" : "Smith"
};
return (
<Container>
<Content>
<List>
{Object.keys(questions).map(function(key) {
return (<ListItem button={true} onPress={this.handleClick}>
<Text>{questions[key]}</Text>
</ListItem>
)
})}
</List>
</Content>
</Container>
);
}
** Final Solution **
handleClick(){
Alert.alert("I got clicked");
}
render() {
var questions = this.props.questions;
return (
<Content>
<List>
{Object.keys(questions).map((eachQuestion) => {
return (
<ListItem key={questions[eachQuestion].id} button={true} onPress={this.handleClick} >
<Body>
<Text>{questions[eachQuestion].question}</Text>
</Body>
</ListItem>
)
})}
</List>
</Content>
);
}
Two errors:
You should brush up on your ES6 arrow function expressions. You aren't calling your handleClick function which is why nothing is happening vs your Alert example where it does work (since you are actually doing something).
You don't define the value for the button prop. The docs say that there is no default value, so it's good practice to define it as true or false.
So to fix your code, you should define your props for ListItem like so:
button={true}
onPress={() => { this.handleClick() }}
OR to make it shorter:
button={true}
onPress={this.handleClick}
I'm also not sure why you are defining button and onPress props on your List component since it's the ListItems that you are trying to click, not the entire List itself. But since that isn't part of the question, I won't address that.
Full example of working code:
import React, { Component } from 'react';
import { Container, Content, List, ListItem, Text } from 'native-base';
import { Alert } from 'react-native';
export default class App extends Component {
constructor(props){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
Alert.alert("I am clicked");
// Call method from parent
//this.props.onPress();
}
render() {
return (
<Container>
<Content>
<List>
<ListItem button={true} onPress={this.handleClick}>
<Text>Simon Mignolet</Text>
</ListItem>
<ListItem button={true} onPress={() => { this.handleClick() }}>
<Text>Nathaniel Clyne</Text>
</ListItem>
<ListItem button={true} onPress={this.handleClick}>
<Text>Dejan Lovren</Text>
</ListItem>
</List>
</Content>
</Container>
);
}
}

ReactJS - onClick to call a component that fadeIn

New web dev I found some nice pure HTML/JQuery template.
https://www.creative-tim.com/
I have to do an app with React and I would like to implement to login modal template on this web site.
https://www.creative-tim.com/product/login-and-register-modal
I'm not sure about the approch I have to make to be able to convert this to React.
I have to handle the onClick on the buttons and make the modal appear.
How can I load the component Modal by change the CSS like the way they do with JQuery ?
import React, { Component } from 'react';
import { Grid, Row, Col, Button } from 'react-bootstrap/lib'
import './Login.css'
import LoginModal from '../LoginModal/LoginModal'
class Login extends Component {
openLoginModal(){
console.log('openLoginModal');
// showLoginForm();
}
openRegisterModal(){
console.log('openRegisterModal');
// showRegisterForm();
}
render() {
return (
<Grid>
<Row>
<Col sm={4}></Col>
<Col sm={4}>
<Button bsClass="btn big-login" data-toggle="modal" onClick={this.openLoginModal}>Log In</Button>
<Button bsClass="btn big-register" data-toggle="modal" onClick={this.openRegisterModal}>Register</Button>
</Col>
<Col sm={4}></Col>
</Row>
<LoginModal />
</Grid>
)
}
}
export default Login
In React you don't need to use CSS to show and hide the modal. You can just use an inline conditional expression with JSX.
first you need to build a RegisterModal...
...Then
import React, { Component } from 'react';
import { Grid, Row, Col, Button } from 'react-bootstrap/lib'
import './Login.css'
import LoginModal from '../LoginModal/LoginModal'
import RegisterModal from '../RegisterModal/RegisterModal';
class Login extends Component {
constructor() {
super();
// create state properties to record open/close for each modal
this.state = {
loginOpen: false,
registerOpen: false
};
}
// toggle your state
openLoginModal(){
this.setState({ loginOpen: true, registerOpen: false });
}
openRegisterModal(){
this.setState({ loginOpen: false, registerOpen: true });
}
render() {
// `{ true && <div /> }` will render div if true
return (
<Grid>
<Row>
<Col sm={4}></Col>
<Col sm={4}>
<Button bsClass="btn big-login" data-toggle="modal" onClick={this.openLoginModal}>Log In</Button>
<Button bsClass="btn big-register" data-toggle="modal" onClick={this.openRegisterModal}>Register</Button>
</Col>
<Col sm={4}></Col>
</Row>
{ this.state.loginOpen &&
<LoginModal />
}
{ this.state.registerOpen &&
<RegisterModal />
}
</Grid>
)
}
}

How to create this react modal the right way?

I have been working on my first Meteor application and am a bit stuck. I want to create my code following the latest guidelines (ES6 and React 15) but I am confused with all the recent changes in Javascript.
I want to add a Bootstrap Modal in my current comments list but can't seem to figure out how to add my content to the modal using the right up to date syntax.
Here is my current code:
In comment.js:
import React from 'react';
import { Row, Col, ListGroupItem, FormControl, Button } from 'react-bootstrap';
import { Bert } from 'meteor/themeteorchef:bert';
import { CommentsModal } from './comments-modal'
export const Comment = ({ comment }) => (
<ListGroupItem key={ comment._id }>
<Row>
<Col xs={ 8 } sm={ 10 }>
<FormControl
type="text"
defaultValue={ comment.title }
/>
</Col>
<Col xs={ 4 } sm={ 2 }>
<Button
bsStyle="danger"
className="btn-block">
Remove Comment
</Button>
</Col>
</Row>
<CommentsModal/>
</ListGroupItem>
);
In Comments-modal.js:
import React, { Component } from 'react';
import { Modal, Button, Tooltip } from 'react-bootstrap';
export class CommentsModal extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false,
};
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>
<Button
bsStyle="primary"
bsSize="large"
onClick={this.open}
>
</Button>
<Modal show={this.state.showModal} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title >Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Text in a modal</h4>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.close}>Close</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
And last comments-list.js:
import React from 'react';
import { ListGroup, Alert } from 'react-bootstrap';
import { Comment } from './comment';
export const CommentsList = ({ comments }) => (
comments.length > 0 ? <ListGroup className="comments-list">
{comments.map((com) => (
<Comment key={ com._id } comment={ com } />
))}
</ListGroup> :
<Alert bsStyle="warning">No comments yet. Please add some!</Alert>
);
CommentsList.propTypes = {
comments: React.PropTypes.array,
};
I manage to get the Modal to show up and work but when I want to display data in it, I can't get it to work. What is the best way to combine both these into one?
Pass the data in props to the CommentsModal and render it as you would normally do.
I try to keep local state out of component when using redux if possible, so to answer your question on making it stateless, I would take the following steps:
Remove the button that opens the modal from the modal.js itself
Remove the actual modal from the modal.js, just put the modal content inside of there.
Change the open modal button to hook into an action creator that sets a prop to open the modal and passes it's content (also set one to close it)
So that looks something like this
<ListGroupItem key={ comment._id }>
<Row>
<Col xs={ 8 } sm={ 10 }>
<FormControl
type="text"
defaultValue={ comment.title }
/>
</Col>
<Col xs={ 4 } sm={ 2 }>
<Button
bsStyle="danger"
className="btn-block">
Remove Comment
</Button>
</Col>
</Row>
<!-- Here is where it changes, -->
<Button
bsStyle="primary"
bsSize="large"
onClick={this.props.openModal(comment)}
>
</Button>
<Modal show={this.props.commentModal} onHide={this.props.closeModal}>
<CommentsModal content={this.props.commentModal} />
</Modal>
Keep in mind, these naming conventions are just for examples sake : use whatever works best for you.
So what happens here is when you click that button you fire this.props.openModal (an action) which does something like this in the reducers -
case actions.OPEN_COMMENT_MODAL:
return state.set('commentModal', action.content);
the close buttons fire the onHide which is linked to the this.props.closeModal action which just does:
case actions.OPEN_COMMENT_MODAL:
return state.set('commentModal', undefined);
So what this allows you to do is have just 1 modal instance and you pass the current comment to it with that button click and open it. The show just checks the truthy value, so you set it back to undefined and it will hide itself.
Then I am passing the prop of content to the modal, so you can then use it inside the modal itself. Again, change the names to whatever works best for you.

Categories