toggle state for multiple elements in Reactjs - javascript

I am using UncontrolledCollapse component from reactstrap and I have multiple static elements to control.
By default I need all the elements to be open by default ( Not collapsed ), on click related individual element will be collapsed.
But I have only one state to manage the behavior, which will open and close all the panels on click.
Below is the code:
import { UncontrolledCollapse, Button, CardBody, Card } from "reactstrap";
class ProductList extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isOpen: true
};
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
return (
<div>
<div
className="filter-options-title"
id="toggler"
onClick={this.toggle}
>
Test
</div>
<UncontrolledCollapse toggler="#toggler" isOpen={this.state.isOpen}>
<div className="filter-options-content">Test Content</div>
</UncontrolledCollapse>
<div
className="filter-options-title"
id="toggler1"
onClick={this.toggle}
>
Test
</div>
<UncontrolledCollapse toggler="#toggler1" isOpen={this.state.isOpen}>
<div className="filter-options-content">Test Content</div>
</UncontrolledCollapse>
</div>
);
}
}

You need to create separate keys in your state. Because currently you're using the same state prop for both of the elements, so if you close one both get closed. I also changed the toggle method (made is accept which elements gets toggled).
import { UncontrolledCollapse, Button, CardBody, Card } from 'reactstrap';
class ProductList extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
toggler: true,
toggler1: true,
}
}
toggle(toggler) {
let togglerStatus = this.state[toggler]; //check the status of the toggle you clicked
this.setState({
[toggler]: !togglerStatus // change the status only for the toggle you clicked
});
}
render() {
return (
<div>
<div className="filter-options-title" id="" onClick={() => this.toggle('toggler')}>
Test
</div>
<UncontrolledCollapse toggler="#toggler" isOpen={this.state['toggler']}>
<div className="filter-options-content">
Test Content
</div>
</UncontrolledCollapse>
<div className="filter-options-title" id="toggler1" onClick={() => this.toggle('toggler1')}>
Test
</div>
<UncontrolledCollapse toggler="#toggler1" isOpen={this.state['toggler1']}>
<div className="filter-options-content">
Test Content
</div>
</UncontrolledCollapse>
</div>
)
}
}

Related

Display/delete component on click

So, im trying to display my component named documentReader inside div with class desktop-app-grid by clicking and icon below, but icon is also a component, i tried doing this by using state, but i don't know how I can do this. I'm dropping my code below with hope someone can help me.
I got this:
<div className="desktop">
<div
className="desktop-app-grid"
>
</div>
<div className="taskbar">
<div className="taskbar-content">
<div className="apps">
<TaskbarAppIcon
appName="documentReader"
icon={icon}
title="My CV"
/>
</div>
<div className="status">
<Clock className="clock" />
</div>
</div>
</div>
</div>
);
}
And on click i want to get displayed in desktop-app-grid like this:
<div
className="desktop-app-grid"
>
<documentReader />
</div>
icon.js (code isn't complete)
class TaskbarAppIcon extends React.Component {
constructor() {
super();
this.state = {
clicked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick = () => {
const icon = document.querySelector("img");
icon.classList.toggle("icon-active");
setTimeout(() => {
icon.classList.toggle("icon-active");
}, 200);
this.setState({
clicked: true
});
}
render(){
const classes = this.props.appName + "Icon icon";
return (
<div className={classes} onClick={this.handleClick}>
<img src={this.props.icon} alt={this.props.appName} title={this.props.title} className="icon-image"></img>
<div className="isActive"></div>
</div>
);
}
}
export default TaskbarAppIcon;
is there any function that works like innerHTML, but isn't a dangerouslyInnerHTML?
what you need to do is move your handleClick and clicked state to the parent component where you rendering TaskbarAppIcon. Being more specific where you have this code:
<div className="desktop">
<div className="desktop-app-grid">
</div>
<div className="taskbar">
<div className="taskbar-content">
<div className="apps">
<TaskbarAppIcon
appName="documentReader"
icon={icon}
title="My CV"
/>
</div>
<div className="status">
<Clock className="clock" />
</div>
</div>
</div>
</div>
So for example, the above code is in you App component, so you need to let it like this:
class App extends React.Component {
constructor() {
super();
this.state = {
clicked: false,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick = () => {
const icon = document.querySelector("img");
icon.classList.toggle("icon-active");
setTimeout(() => {
icon.classList.toggle("icon-active");
}, 200);
this.setState({
clicked: !this.state.clicked,
});
};
render() {
return (
<div className="desktop">
<div className="desktop-app-grid">
// here's the trick, if your clicked state is TRUE it will show <documentReader />
{this.state.clicked && <documentReader />}
</div>
<div className="taskbar">
<div className="taskbar-content">
<div className="apps">
<TaskbarAppIcon
// Here you are specifying that TaskbarAppIcon has a prop handleClick and its a function
handleClick={this.handleClick}
appName="documentReader"
icon={icon}
title="My CV"
/>
</div>
</div>
</div>
</div>
);
}
}
And in your TaskbarAppIcon component you just need to change
<div className={classes} onClick={this.handleClick}>
to
<div className={classes} onClick={this.props.handleClick}>

How to check if some text within some tag is visible on the webpage using javascript or react?

i am new to programming and want to check if the h3 tag with text "hello" is present in the document.
consider the html below,
<div class= "parent_div">
<div class="child_div">
<h3>hello</h3>
</div>
</div>
now i want to check if the h3 element within parent_div class exists in the document that i am currently viewing. how can i do that using javascript.
i have tried using document.querySelector("div.parent_div h3[text()="hello"]")
this does not work. how can i do it. could someone help me with this. thanks.
As per your comment, in the case of React, if you don't want a side panel (or drawer) to be expandable until an image loads, you can use the onload event callback to set the state of your drawer to expandable only after it loads.
class Drawer extends React.Component {
constructor(props) {
super(props);
this.state = {
expandable: props.expandable,
expanded: false
};
}
render() {
const expandButton = (
<button onClick={() => this.setState({ expanded: !this.state.expanded })}>
{this.state.expanded ? "Click to close..." : "Click to expand!"}
</button>
);
const drawerContent = this.state.expanded ? (
<p>I'm expanded!</p>
) : (
<p>I'm not expanded :( </p>
);
return (
<div>
{expandButton}
{drawerContent}
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
drawerExpandable: false
};
}
render() {
return (
<div>
<Drawer expandable={this.state.drawerExpandable} />
<img
src={"https://placekitten.com/200/300"}
onLoad={() => this.setState({ drawerExpandable: true })}
/>
</div>
);
}
}
ReactDOM.render(<App />, 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>
This could be an approach using vanilla js:
const checkExist = document.querySelector(".parent_div .child_div h3").innerText == "hello"
console.log(checkExist)
<div class= "parent_div">
<div class="child_div">
<h3>hello</h3>
</div>
</div>
But if you are using React.js better use component state.
<div class= "parent_div">
<div class="child_div">
<h3>hello</h3>
</div>
</div>
<script type="text/javascript">
var h3=document.getElementsByTagName('h3');
alert(h3.length===0?'not exist':'exists, and text is :'+h3[0].innerHTML);
</script>

Update component with data from parent in React

In my react app I have this child component that inherits data from its parent. However, it does not update the page with new data from the child component when a relevant anchor link is clicked.
Here's my build - https://suite-search-lk.surge.sh/result/369523
From the link above if you click on a suggested card h1 element it just updates the URL with the id but does not update the page with the relevant card data from that id.
Any idea how I can fix this? Do I have to force the component to re-update?
Parent component (Card Wrapper)
class CardWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
stories: []
};
}
componentDidMount() {
axios
.get(API)
// .then(response => console.log(response))
// get our stories array, check it and then change state to contain our stories
.then(data => {
let stories;
if (data.data.stories && data.data.stories) {
if (Array.isArray(data.data.stories)) {
stories = data.data.stories;
} else {
stories = [data.data.stories];
}
} else {
stories = [];
}
this.setState({
stories: stories
});
});
}
render() {
return (
<CardWrapperDiv>
<div className="headingWrapper">
<div className="heading"> Suggested for you</div>
</div>
<Cards>
{this.state.stories.map(story => {
return (
<Card
title={story.content.title}
img={story.content.img}
description={story.content.description}
deadline={story.content.deadline_date}
tags={story.content.tags}
key={story.id}
id={story.id}
/>
);
})}
</Cards>
</CardWrapperDiv>
);
}
}
export default CardWrapper;
Child component
class Card extends React.Component {
render() {
return (
<CardDiv>
<div className="cardbox">
<div className="cardDetails">
<div className="headlineText">
<Link to={`/result/${this.props.id}`}> {this.props.title} </Link>
</div>
<div className="headlineSub">Colombo, Sri Lanka</div>
<div className="headlineDes">{this.props.description}</div>
<div className="textRemain">
{" "}
Deadline date: {this.props.deadline}
</div>
<div className="buttonRow">
<button className="downloadBtn">Download</button>
<button className="viewBtn">View</button>
</div>
</div>
<div className="cardimgwrapper">
<div className="cardimg">
<img src={this.props.img} alt="some title" />
</div>
</div>
</div>
</CardDiv>
);
}
}
export default Card;
Sorry it seems I have figured this out using the following post - Can you force a React component to rerender without calling setState?
Although I'm not exactly sure if it's the best way to go about it.
Essentially I used an OnClick listener to run a function and forces a re-render of the entire component.
Hope this can help someone else :)
class Card extends React.Component {
handleButtonClick() {
this.forceUpdate();
}
render() {
return (
<CardDiv>
<div className="cardbox">
<div className="cardDetails">
<div className="headlineText">
<Link to={`/result/${this.props.id}`} onClick={this.handleButtonClick}> {this.props.title} </Link>
</div>
<div className="headlineSub">Colombo, Sri Lanka</div>
<div className="headlineDes">{this.props.description}</div>
<div className="textRemain">
{" "}
Deadline date: {this.props.deadline}
</div>
<div className="buttonRow">
<button className="downloadBtn">Download</button>
<button className="viewBtn">View</button>
</div>
</div>
<div className="cardimgwrapper">
<div className="cardimg">
<img src={this.props.img} alt="some title" />
</div>
</div>
</div>
</CardDiv>
);
}
}
export default Card;
U have to use ur child component as a pure component. PureComponent Update when ur props change.
class Card extends React.PureComponent {
handleButtonClick() {
this.forceUpdate();
}
render() {
return (
<CardDiv>
.....
.....
</CardDiv>
);
}
}
export default Card;

How do I properly pass function props on another component with React.Component?

I am introducing my self in es6+, I have a hard time trying to pass a function props to another component.
This is my code:
class ProductList extends React.Component {
constructor(props) {
super(props);
this.onVote = this.handleProductUpVote.bind(this);
}
handleProductUpVote(productId) {
console.log(productId +" was upvoted.")
}
render() {
const products = Data.map((product) => {
return (
<Product
key={'product-'+product.id}
id={product.id}
title={product.title}
description={product.description}
url={product.url}
votes={product.votes}
submitter_avatar_url={product.submitter_avatar_url}
product_image_url={product.product_image_url}
onVote={this.handleProductUpVote}
/>
);
});
return (
<div className="ui items">
{products}
</div>
);
}
}
I want to pass the function onVote in this component(Product)
class Product extends React.Component {
handleUpVote() {
this.props.onVote(this.props.id).bind(this) /* the error is here, I am trying
to pass the id props, and invoke the onVote prop here */
}
render() {
return (
<div className="item">
<div className="image">
<img src={this.props.product_image_url} />
</div>
<div className="middle aligned content">
<div className="description">
<a onClick={this.handleUpVote}>
<i className="large caret up icon"/>
</a>
{this.props.votes}
</div>
<div className="description">
<a href={this.props.url}>
{this.props.title}
</a>
</div>
<div className="extra">
<span> Submitted by: </span>
<img
className="ui avatar image"
src={this.props.submitter_avatar_url}
/>
</div>
</div>
</div>
);
}
}
I have no problem with other props here. I am trying to invoke the function on handleUpVote, I used bind with it, but I can't make it work. Help?
You have to use bounded handleProductUpVote method when you pass it to Product component.
As you can see in constructor, you already bound it and assigned to this.onVote property.
There are 2 solutions:
You should use onVote={this.onVote} in render method.
Change the name of property onVote in constructor to this.handleProductUpVote. And you end up with this.handleProductUpVote = this.handleProductUpVote.bind(this) and leave assignment in render method (i.e. onVote={this.handleProductUpVote})
More info at http://reactkungfu.com/2015/07/why-and-how-to-bind-methods-in-your-react-component-classes/
Update:
And update your Product class:
class Product extends React.Component {
constructor(props) {
super(props);
this.handleUpVote = this.handleUpVote.bind(this);
}
handleUpVote() {
this.props.onVote(this.props.id)
}
// the render method
}
Remove the bind in handleUpVote() in your Product component and just invoke it like this.props.onVote(this.props.id);

Insert a component when clicked and remove the componet when click other than that component

ContentWithFooter has list of Card's. And each Card has a menu to flag or delete the Card (article). When the icon is clicked, I want to insert the menu (<DeleteFlagPost/>) for that Card only and not all the cards. Also after the menu is inserted, when clicked other than that menu, remove it from the menu component. How do I do that? I know this is a long list of HOW, but your help will really help me understand more. Thank you.
ContentWithFooter.js:
class ContentWithFooter extends React.Component {
render() {
return(
<div>
<Card/>
<Card/>
<Card/>
</div>
);
}
}
Card.js
class Card extends React.Component {
handleDeleteFlag() {
return(
// GET THE CLICKED OBJECT OF THIS CARD AND INSERT <DeleteFlagPost/>???
);
}
render() {
return(
<article class="article">
...
<div class="delete-flag-post-btn">
<i class="fa fa-ellipsis-v" aria-hidden="true" onClick={this.handleDeleteFlag}></i>
// INSERT THE <DeleteFlagPost/> here when clicked on I element...
</div>
...
...
</article>
);
}
}
Keep active card in the contentwithfooter
class ContentWithFooter extends React.Component {
updateMenuForCards(cardNumActive) {
this.setState({
cardNumActive
})
}
render() {
return(
<div>
<Card cardNumber= 0 showMenu={cardNumActive === 0} updateMenuForCards={updateMenuForCards}/>
<Card cardNumber= 1 showMenu={cardNumActive === 0} updateMenuForCards={updateMenuForCards}/>
<Card cardNumber= 2 showMenu={cardNumActive === 0} updateMenuForCards={updateMenuForCards}/>
</div>
);
}
}
class Card extends React.Component {
constructor() {
super(props);
}
handleDeleteFlag() {
this.props.updateMenuForCards(this.props.cardNumber)
}
renderDeleteFlag() {
if (this.props.showMenu) {
return (<DeleteFlag/>)
}
}
render() {
return(
<article class="article">
...
<div class="delete-flag-post-btn">
<i class="fa fa-ellipsis-v" aria-hidden="true" onClick={this.handleDeleteFlag}></i>
{this.renderDeleteFlag()}
</div>
...
...
</article>
);

Categories