I'm using React Tippy - a React component based on Tippy.js. I want to use the documented setIsOpen method - but it's not working.
TypeError: setIsOpen is not a function.
Can't seem to find any documentation or issues related to this. Any ideas?
My code is:
<Tooltip
position="right"
animation="scale"
arrow="true"
arrowSize="big"
theme="light"
trigger="click focus"
interactive
open={open}
html={(
<div className="tooltip-body">
<span className="info icon-sm-info"></span>
<span className="close" onClick={() => { setIsOpen(false) }}>×</span>
<h5>Hello</h5>
<div>Tooltip Content</div>
</div>
)}
>
<span className="icon-sm-info">Hello</span>
</Tooltip>
your state :
this.state = {open : false}
setIsOpen = () => {
this.setState(state => { open : true});
}
Updated Code
<Tooltip
position="right"
animation="scale"
arrow="true"
arrowSize="big"
theme="light"
trigger="click focus"
interactive
open={open}
html={(
<div className="tooltip-body">
<span className="info icon-sm-info"></span>
<span className="close" onClick={() => { this.setIsOpen() }}>×</span>
<h5>Hello</h5>
<div>Tooltip Content</div>
</div>
)}
>
<span className="icon-sm-info">Hello</span>
</Tooltip>
I found out how to do it by using state.
class ToolTip extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false
}
}
setIsOpen = (option) => {
this.setState({
open: option
});
}
<span className="close" onClick={() => {this.setIsOpen(false)}}>×</span>
Related
I have a dropdown menu in my react app just like this:
and you see there is "Open modal" where I have problem with:
The issue is when I click on it, the modal content shows up inside of the the list which is another component. the code is like:
const App = () => {
return (
<CRow>
<CCol xs={12}>
<CNav>
<CNavItem>
</CNavItem>
<CDropdown variant="nav-item">
<CDropdownToggle color="secondary">Dropdown button</CDropdownToggle>
<CDropdownMenu>
<CDropdownItem><Modal parentSelector={() => document.querySelector('#root')}> </Modal> </CDropdownItem>
<CDropdownItem href="#">Another action</CDropdownItem>
<CDropdownItem href="#">Something else here</CDropdownItem>
</CDropdownMenu>
</CDropdown>
<CNavItem>
<CNavLink href="#">Link</CNavLink>
</CNavItem>
<CNavItem>
<CNavLink href="#" disabled>
Disabled
</CNavLink>
</CNavItem>
</CNav>
</CCol>
</CRow>
)
}
export default App
and here is my modal component:
const display = {
display: 'block'
};
const hide = {
display: 'none'
};
class Modal extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
toggle: false
}
}
toggle(event) {
this.setState((prevState) => ({
toggle: !prevState.toggle
}));
}
render() {
var modal = [];
modal.push(
<div className="modal" style={this.state.toggle ? display : hide}>
<div className="modal-content">
<h4>modal content</h4>
</div>
<div className="modal-footer">
<a className="btn-flat" onClick={this.toggle}>Agree</a>
</div>
</div>
);
return (
<div>
<a className="btn" onClick={this.toggle}>{this.state.toggle ? 'Close modal' : 'Open modal'}</a>
{modal}
</div>
);
}
}
export default Modal
I tried QuerySelector, but didn't work. How can I control which DIV or component, modal can show up?
I'm using functional component in react js , my onClick function triggers with component rendering without click on my li element ;
this is my parent component that passed the handleCallDetails function as props to child component:
export default function Cartable(){
const [items , setItems] = useState(null);
const [details , setDetails] = useState(null);
function handleCallDetails(id){
if(items !== null && details === null){
let d = items.find(x => {
return x.id === id;
});
}
}
useEffect(() => {
axios.get(`/workflows/${mode}` ,{
params : {
OrganizationId : "FE905B40-DA6E-4A81-8A4F-B447AA6B0EA3" ,
Type : 2 ,
sortorder : "desc" ,
pageIndedx : 1 ,
pageSize : 10
}
}).then(response => {
// console.log('response : ***************** ' , response);
setItems(response.data.data);
}).catch(error => {
console.log('error : ****************** ' , error);
});
} , [mode]);
return (
<Grid container spacing={2}>
<Grid item xs={12} sm={4} md={3}>
<div className="drt_RightSide drt_segment">
<h4 className="drt_RightSideTitle">
<i className="far fa-inbox"></i>
کارتابل
</h4>
<ul>
{/* <li>
<i class="far fa-inbox"></i>
<span>درخواست ها</span>
</li> */}
<li onClick={() => {setMode('pending');}}>
<i className="fas fa-exclamation"></i>
<span><FormattedMessage id="CARTABLE_PENDING" /></span>
<span className="drt_badge_warning drt_NotifNum">5</span>
</li>
<li onClick={() => {setMode('approved');}}>
<i className="far fa-check"></i>
<span>تایید شده</span>
</li>
<li onClick={() => {setMode('rejected');}}>
<i className="far fa-times"></i>
<span>رد شده</span>
<span className="drt_badge_error drt_NotifNum">7</span>
</li>
<li>
<i className="far fa-bell"></i>
<span>خارج از فرآیند</span>
</li>
</ul>
</div>
</Grid>
<Grid item xs={12} sm={8} md={9}>
<div className="drt_LeftSide drt_segment"> */}
{/* cartbale list */}
<CartableList
items={items}
callDetails={handleCallDetails}/>
</div>
</Grid>
</Grid>
);
}
and it is my child compnent that use onClick function that named callDetails:
export default function CartableList(props){
const [showbox , setShowbox] = useState(false);
const [age, setAge] = useState('');
const handleChange = (event) => {
setAge(event.target.value);
};
function handleFilterBox(){
setShowbox(!showbox);
}
return (
<Fragment>
{/* cartable list */}
<div style={{direction : "ltr"}}>
<Scrollbars style={{ height: 400 }}>
{
props.items && props.items !== undefined ?
props.items.map(function(item , index){
return (
<div className="drt_clearfix drt_CartableItem" key={index} onClick={(props.callDetails)(item.id)}>
{/* <div className={clsx(drt_ItemStar , item.star ? drt_IsStared : '')}>
<span><i className={clsx(item.star ? "fas fa-star" : "fal fa-star")}></i></span>
</div> */}
<div className="drt_ItemImg">
<span>
<img alt={userImg} src={item.pictureUrl !== undefined && item.pictureUrl !== null ? item.image : userImg} />
</span>
</div>
<div className={clsx("drt_ItemName" , !item.isSeen ? "drt_IsNotSeen" : '')}>
{item.issuerFirstName}
<br />
{item.issuerLastname}
</div>
<div className="drt_ItemIcon">
<Tooltip title={(props.moduleType)(item.type).name}>
<span className={item.isSeen ? "drt_badge_default" : "drt_badge_primary"}>
<i className={(props.moduleType)(item.type).icon} />
</span>
</Tooltip>
</div>
<div className={clsx("drt_ItemDesc" , !item.isSeen ? "drt_IsNotSeen" : '')}>
{item.objectTitle}
</div>
<div className="drt_ItemStatus">
<span className={(props.stateClass)(item.status)}>
{(props.stateTitle)(item.status)}
</span>
</div>
<div className={clsx("drt_ItemDate" , !item.isSeen ? "drt_IsNotSeen" : '')}>
<p>
<span>
{item.issuerTime}
</span>
<span>
{item.issuerDate}
</span>
</p>
<i className="fal fa-clock" />
</div>
</div>
);
}) : ''
}
</Scrollbars>
</div>
</Fragment>
);
}
please help me to solve this problem without convert my functional component to class component and binding my function
The correct way is this. You need to use arrow function or else react will understand that you want to execute the function at load
wrong
<div className="drt_clearfix drt_CartableItem" key={index} onClick={(props.callDetails)(item.id)}>
correct
<div className="drt_clearfix drt_CartableItem" key={index} onClick={() => props.callDetails(item.id)}>
Change from
onClick={() => {setMode('rejected');}}
to
onClick={() => setMode('rejected')}
Also
<div className="drt_clearfix drt_CartableItem" key={index} onClick={() => props.callDetails(item.id)}>
But where did you define the const [mode, setMode] state
It seems you exec your callback function immediately as it renders based on your code:
onClick={(props.callDetails)(item.id)}
It's supposed to be:
onClick={() => props.callDetails(item.id)}
Is that the issue?
It's mainly because of piece of code onClick={(props.callDetails)(item.id)} in your child component. This code is actually executing callDetails function and passing the item.id value immediately. One way to handle this is to wrap your function.
onClick={() => {props.callDetails(item.id)}}
A simple reason as to why onClick is not called when it is wrapped is because it is not directly passing in any value when initialised.
I have a two react bootstrap buttons within a dropdown and I am trying to understand why the onClick event is not working. The button are returned from function. I can get the event to fire if I just use Button in the main render function. I'd like to know why this is happening. Is returning the component from a function ok? Or do I need to use a class;
Code:
{this.state.filterDropdowns.map((value, indexNo) =>
<Dropdown
className="button"
key={`${indexNo}`}
>
<Dropdown.Toggle variant="light" size="sm">
{value.name}
</Dropdown.Toggle>
<Dropdown.Menu className="custommenu">
{value.data.result.map((input, index) =>
<div key={`${index}`}>
<input
key={`${index}`}
id={input}
type="checkbox"
defaultChecked
onClick={(e) => this.clickFilterBox(e, indexNo)}
>
</input>{" " + input}<br></br>
</div>
)}
<Dropdown.Divider />
<CustomButtons
length={value.data.result.length}
index={indexNo}
onClear={(e) => this.onClear(e)}
onSelect={(e) => this.onSelect(e)}
>
</CustomButtons>
</Dropdown.Menu>
</Dropdown>
)}
Custom button function
const CustomButtons = function(props) {
if (props.length < 10) {
return(<div></div>);
} else {
return (
<div >
<div className="select-div">
<Button
size="sm"
onClick={props.onSelect}
>
Select
</Button>
</div>
<div className="clear-div">
<Button
size="sm"
onClick={props.onClear}
>
Clear
</Button>
</div>
</div>
);
}
}
You need to change your implementation as follows:
<CustomButtons
length={value.data.result.length}
index={indexNo}
onClear={this.onClear.bind(this)}
onSelect={this.onSelect.bind(this)}
/>
Then in the function implement it like this:
<Button
size="sm"
onClick={(e) => props.onSelect(e)}
>
Select
</Button>
For anyone interested this is a solution:
Created a class for custom buttons
import React from 'react';
import {Button} from 'react-bootstrap';
export default class CustomButtons extends React.Component {
constructor(props) {
super(props);
this.state = {
length: props.length,
onClear: props.onClear,
onSelect: props.onSelect
}
}
render() {
if (this.state.length < 10) {
return(<div></div>);
} else {
return (
<div >
<div className="select-div">
<Button
size="sm"
onClick={this.state.onSelect}
>
Select
</Button>
</div>
<div className="clear-div">
<Button
size="sm"
onClick={this.state.onClear}
>
Clear
</Button>
</div>
</div>
);
}
}
}
Then in main app component
<CustomButtons
length={value.data.result.length}
index={indexNo}
onClear={(e) => this.onClear(e)}
onSelect={(e) => this.onSelect(e)}
>
</CustomButtons>
I have a React-Redux application and I'm having issues launching a function on component click. When the logout button is clicked, it should trigger onClick={this.handleLogout}. However, Redux dev tools and adding a console log in the handleLogout prove that the function never executes.
Here are the two components, a standard navigation bar and a custom button component. I've left out some imports to shorten the code.
Navigation Bar
class Navigation extends React.Component {
handleLogout = () => {
this.props.logOut();
this.props.history.push("/login");
}
render() {
return (
<nav>
<span className="nav-left">
<Link className="light" to="/">
Home
</Link>
</span>
<span className="nav-right">
{this.props.authenticated ? (
<>
<Button to="/account">Account</Button>
<Button onClick={this.handleLogout} buttonStyle="danger">
Log Out
</Button>
</>
) : (
<Button to="/login">Login</Button>
)}
</span>
</nav>
);
}
}
Navigation.propTypes = {
authenticated: PropTypes.bool.isRequired,
logOut: PropTypes.func.isRequired
};
export default withRouter(Navigation);
Button
export default class Button extends Component {
state = {
classList: classNames({
button: true,
button_accent: this.props.buttonStyle === "accent",
button_danger: this.props.buttonStyle === "danger"
})
};
render() {
return (
<div className="button_container">
<div
className={classNames({
button_wrapper: true,
center: !this.props.left && !this.props.right,
left: this.props.left,
right: this.props.right
})}
>
{
this.props.to ? (
this.props.regular ? (
<a href={this.props.to} className={this.state.classList}>
{this.props.children}
</a>
) : (
<Link to={this.props.to} className={this.state.classList}>
{this.props.children}
</Link>
)
) : (
<span className={this.state.classList}>
{this.props.children}
</span>
)
}
</div>
</div>
);
}
}
Component Dev Tools
component dev tools
You forgot to pass onClick to the span that represents a button without a link
export default class Button extends Component {
state = {
classList: classNames({
button: true,
button_accent: this.props.buttonStyle === "accent",
button_danger: this.props.buttonStyle === "danger"
})
};
render() {
return (
<div className="button_container">
<div
className={classNames({
button_wrapper: true,
center: !this.props.left && !this.props.right,
left: this.props.left,
right: this.props.right
})}
>
{
this.props.to ? (
this.props.regular ? (
<a href={this.props.to} className={this.state.classList}>
{this.props.children}
</a>
) : (
<Link to={this.props.to} className={this.state.classList}>
{this.props.children}
</Link>
)
) : (
<span onClick={this.props.onClick} className={this.state.classList}>
{this.props.children}
</span>
)
}
</div>
</div>
);
}
}
I am displaying a list of items from database and for each item, I have a button "Show more/less". When this is clicked, I want to show/hide the extra content with a nice slide down/up effect. I have implemented this functionality without the slide down/up effect, but want to use React Collapse to make it more user-friendly.
Here's the component where I am trying to implement the React Collapse functionality:
import React from 'react';
import ReactDOM from 'react-dom';
//import axios from 'axios';
import NewPost from './NewPost';
import {Collapse} from 'react-collapse';
class Posts extends React.Component {
constructor(props) {
super(props);
this.toggleClass= this.toggleClass.bind(this);
this.state = {
activeIndex: null
}
}
toggleClass(index, e) {
this.setState({ activeIndex: this.state.activeIndex === index ? null : index });
};
moreLess(index) {
if (this.state.activeIndex === index) {
return (
<span>
<i className='fas fa-angle-up'></i> Less
</span>
);
} else {
return (
<span>
<i className='fas fa-angle-down'></i> More
</span>
);
}
}
render () {
let content;
if (this.props.loading) {
content = 'Loading...';
} else {
content = this.props.posts.map((post, key) => {
return(
<li key={key}>
<div>
<span>{post.id}</span>
<span>{post.message}</span>
<button className="btn btn-primary btn-xs" onClick={this.toggleClass.bind(this, key)}>
{this.moreLess(key)}
</button>
</div>
<Collapse isOpened={true}>
<div className={'alert alert-info msg '+(this.state.activeIndex === key ? "show" : "hide")}>
{post.message}
</div>
</Collapse>
</li>
)
});
}
return (
<div>
<h1>Posts!</h1>
<div className="row">
<div className="col-md-6">
<ul>
{content}
</ul>
</div>
</div>
</div>
);
}
}
export default Posts
But when I click on the More/less button, the content in Collapse doesn't appear - after clicking the button nothing happens.
What am I missing here yet?
if you're using function and hooks I recommend this
import { Collapse } from "react-collapse";
import classNames from "classnames";
import React, { useState} from 'react';
export default function yourFunction() {
const [activeIndex, setActiveIndex] = useState(null);
return(
{groups.map((group, index) => (
<button className="btn btn-primary navbar-toggler"
type="button"
data-toggle="collapse"
onClick={event => setActiveIndex(
activeIndex === index ? null : index
)}
data-target="#collapseExample"
aria-expanded="false"
aria-controls="collapseExample">
[CLICK HERE]
</button>
<Collapse isOpened={activeIndex === index}>
<div
className={classNames("alert alert-info msg", {
show: activeIndex === index,
hide: activeIndex !== index
})}
>
<a>[YOUR COLLAPSE CONTENT]</a>
</div>
</Collapse>
)
}
You didn't bind correct check to <Collapse isOpened={true}>. Instead of true, you should put (this.state.)activeIndex === index (current item index) like this:
<Collapse isOpened={this.state.activeIndex === index}>
So it can actually collapse due to activeIndex. I've made codesandbox for you so you can make sure it works: https://codesandbox.io/s/jzx44ynyqw
But I think this is the most important part of it (note that your index was called key, I just renamed it for convenience):
<li key={index}>
<div>
<p>{post.title}</p>
<Collapse isOpened={activeIndex === index}>
<div
className={classNames("alert alert-info msg", {
show: activeIndex === index,
hide: activeIndex !== index
})}
>
{post.message}
</div>
</Collapse>
<button
className="btn btn-primary btn-xs"
onClick={this.toggleClass.bind(this, index)}
>
{this.moreLess(index)}
</button>
</div>
</li>