I have a Notification class to manage creating/removing Notifications from the UI. I can successfully show/hide notifications, but the notifications do not animate in/out.
import React from 'react'
import addons from 'react/addons'
const ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
let id = 0
export default class Notification {
constructor (content, className) {
let div = document.createElement('div')
document.body.appendChild(div)
let onClose = () => React.unmountComponentAtNode(div)
React.render(
<NotificationElement className={ className } id={ id++ } onClose={ onClose }>
{ content }
</NotificationElement>,
div
)
}
}
class NotificationElement extends React.Component {
constructor(_) {
super(_)
}
render() {
const className = `Notification ${ this.props.className }`
return (
<ReactCSSTransitionGroup transitionName="slideIn">
<div className={ className } key={ this.props.id }>
{ this.props.children }
<a className="close-button" onClick={ this.props.onClose }>×</a>
</div>
</ReactCSSTransitionGroup>
)
}
}
ReactCSSTransitionGroup will only animate its children when they are added or removed from its props. In your case, the children prop of your ReactCSSTransitionGroup never changes.
Here's an example of what you could do instead:
let notificationsContainer = document.createElement('div');
document.body.appendChild(notificationsContainer);
let notifications = [];
function renderNotifications() {
React.render(<Notifications notifications={notifications} />, notificationsContainer);
}
class Notifications extends React.Component {
render() {
return (
<ReactCSSTransitionGroup transitionName="slideIn">
{this.props.notifications.map(notification =>
<NotificationElement
key={ notification.id }
className={ notification.className }
onClose={ notification.onClose }
children={ content }
/>
)}
</ReactCSSTransitionGroup>
);
}
}
let id = 0
export default class Notification {
constructor (content, className) {
let notification = {
id: id++, content, className,
};
notification.onClose = () => {
notifications.splice(notifications.indexOf(notification), 1);
renderNotifications();
};
notifications.push(notification);
renderNotifications();
}
}
class NotificationElement extends React.Component {
constructor(_) {
super(_)
}
render() {
const className = `Notification ${ this.props.className }`
return (
<div className={ className }>
{ this.props.children }
<a className="close-button" onClick={ this.props.onClose }>×</a>
</div>
)
}
}
Every time a notification is added or removed, its corresponding element is added or removed from the children prop of ReactCSSTransitionGroup, which can then animate it in or out properly.
You can add transitionAppear={true} to your <ReactCSSTranstionGroup /> to make it animate the initial render.
It is disabled by default: https://github.com/facebook/react/blob/master/src/addons/transitions/ReactCSSTransitionGroup.js#L38
Related
I am creating a program with React Js, where I need to write something in prompts and it needs to appear in window. Now I have created the function of adding prompts and pasting it in window, but it's giving an error.
please help me if you can : )
export default class Clock extends React.Component {
state = {items: ['Hakob', 'Arman']};
Add(){
const newitems = this.state.items.concat([prompt('a')])
this.setState({items:newitems})
}
render(){
return <div>
<Clock2/>
</div>
}
}
class Clock2 extends React.Component {
render(){
return(
<>
<button onClick={this.Add}>click</button>
<div> {this.state.items.map((e, i) => {
return <div key = {e + i}> {e} </div>
} )} </div>
</>
)
}
}
you have not defined any state in class clock2 so, the line # 798 giving you an error for cannot read property of items as it is not defined in class
class Clock2 extends React.Components {
state = {
items : //
}
}
and the second error is you are trying to return in the return function that is not correct if you want to map items you have to define map function in render
{
const items = this.state.items.map((e,i ) => {
//
}
return (
<items/>
)
So let's write your code here.
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return <div><Clock2/><div>
}
}
class Clock2 extends React.Component {
render() {
return (
<>
<button onClick={this.Add}>Click</button>
<div>{ this.state.items.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}
You made a mistake, state are internals to component, as well as method.
This should work
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return (
<>
<button onClick={this.Add}>Click</button>
<div>{ this.state.items.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}
Or you can pass values from top component to his child.
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return (<div><Clock2 add={this.Add} values={this.state.items}/><div>)
}
}
class Clock2 extends React.Component {
render() {
return (
<>
<button onClick={this.prop.add}>Click</button>
<div>{ this.props.values.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}
Edit: I have included the full code for better clarity
I am not sure if this is possible. I am trying to pass an onClick event via props but the click event does not work.
The parent component looks like this:
import React from 'react'
import { getProductsById } from '../api/get'
import Product from './Product'
import { instanceOf } from 'prop-types'
import { withCookies, Cookies } from 'react-cookie'
class Cart extends React.Component {
static propTypes = {
cookies: instanceOf(Cookies).isRequired
}
constructor(props) {
super(props)
const { cookies } = props;
this.state = {
prods: cookies.get('uircartprods') || '',
collapsed: true,
total: 0,
}
this.expand = this.expand.bind(this)
this.p = [];
}
componentDidMount() {
this.getCartProducts()
}
handleClick = (o) => {
this.p.push(o.id)
const { cookies } = this.props
cookies.set('uircartprods', this.p.toString(), { path: '/' , sameSite: true})
this.setState({prods: this.p })
console.log('click')
}
getCartProducts = async () => {
let products = []
if (this.state.prods !== '') {
const ts = this.state.prods.split(',')
for (var x = 0; x < ts.length; x++) {
var p = await getProductsById(ts[x])
var importedProducts = JSON.parse(p)
importedProducts.map(product => {
const prod = <Product key={product.id} product={product} handler={() => this.handleClick(product)} />
products.push(prod)
})
}
this.setState({products: products})
}
}
expand(event) {
this.setState({collapsed: !this.state.collapsed})
}
handleCheckout() {
console.log('checkout clicked')
}
render() {
return (
<div className={this.state.collapsed ? 'collapsed' : ''}>
<h6>Your cart</h6>
<p className={this.state.prods.length ? 'hidden' : ''}>Your cart is empty</p>
{this.state.products}
<h6>Total: {this.props.total}</h6>
<button onClick={this.handleCheckout} className={this.state.prods.length ? '' : 'hidden' }>Checkout</button>
<img src="./images/paypal.png" className="paypal" alt="Paypal" />
<a className="minify" onClick={this.expand} alt="My cart"></a>
<span className={this.state.prods.length ? 'pulse' : 'hidden'}>{this.state.prods.length}</span>
</div>
)
}
}
export default withCookies(Cart)
The Product component:
import React from 'react';
class Product extends React.Component {
constructor(props) {
super(props);
this.state = {
showDetails: false,
showModal: false,
cart: []
}
this.imgPath = './images/catalog/'
}
render() {
return (
<div className="Product">
<section>
<img src={this.imgPath + this.props.product.image} />
</section>
<section>
<div>
<h2>{this.props.product.title}</h2>
<h3>{this.props.product.artist}</h3>
<p>Product: {this.props.product.product_type}</p>
<h4>${this.props.product.price}</h4>
<button className="button"
id={this.props.product.id} onClick={this.props.handler}>Add to cart</button>
</div>
</section>
</div>
)
}
}
export default Product
If I log this.props.handler I get undefined. Everything works apart from the click handler, I was wondering if it might have something to with the async function. I am very new to React, there are still some concepts I'm not sure about, so any help is appreciated.
Okay, I see a few issues here.
First, there is no need to call this.handleClick = this.handleClick.bind(this) in the constructor, because you are using an arrow function. Arrow functions do not have a this context, and instead, accessing this inside your function will use the parent this found in the Class.
Secondly, it is wrong to store components in state. Instead, map your importedProducts inside the render function.
Thirdly, the issue with your handler is that this.props.handler doesn't actually call handleClick. You will notice in the definition handler={(product) => this.handleClick} it is returning the function to the caller, but not actually calling the function.
Try this instead.
class Product extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<button className="button" id={this.props.product.id} onClick={this.props.handler}>
Add to cart
</button>
</div>
);
}
}
export default Product;
import Product from './Product'
class Cart extends React.Component {
constructor(props) {
super(props);
}
handleClick = (o) => {
console.log('click');
};
render() {
return (
<div>
{importedProducts.map((product) => {
return <Product key={product.id} product={product} handler={() => this.handleClick(product)} />;
})}
</div>
);
}
}
export default Cart;
I have a list of ids (integer) and I have multiple components.
After a request to my API, the component receives a list of ids that should already be active.
I want to simulate a click on each element with the same id as the one in my array. I know I can use refs to do that, but I don't undertstand how to make it works with a list of elements.
Here's my code :
import React, { Component } from 'react'
import InterestBox from './InterestBox'
import Axios from 'axios'
export class InterestList extends Component {
constructor(props) {
super(props);
this.state = {pinterests: []}
}
componentDidMount() {
Axios.get('http://localhost:8000/api/interests')
.then((success) => {
this.setState({pinterests: success.data.data.interests});
})
}
componentDidUpdate(prevProps) {
console.log(JSON.stringify(prevProps));
console.log(JSON.stringify(this.props))
if(this.props.alreadyChecked != prevProps.alreadyChecked) {
this.props.alreadyChecked.forEach((item) => {
console.log(item)
})
}
}
render() {
return (
<React.Fragment>
{Object.keys(this.state.pinterests).map((interest) => {
var pinterest = this.state.pinterests[interest];
return <InterestBox id={pinterest.id} onClick={this.props.onClick} icon={pinterest.picture_src} title={pinterest.name} />
})}
</React.Fragment>
)
}
}
export default InterestList
import React, { Component } from 'react'
export class InterestBox extends Component {
constructor(props) {
super(props);
this.images = require('../../img/interests/*.svg');
this.state = {activated: false};
this.interest_box_content = React.createRef();
this.interest_text = React.createRef();
this.handleClick = this.handleClick.bind(this);
this.updateDimensions = this.updateDimensions.bind(this);
}
handleClick() {
this.props.handleClick(this.props.id, this.props.title);
this.setState(prevState => ({
activated: !prevState.activated
}))
}
updateDimensions() {
console.log((window.getComputedStyle(this.refs.interest_box_content).width))
this.refs.interest_text = (window.getComputedStyle(this.refs.interest_box_content).width)
}
render() {
return (
<div className="column is-one-fifth-desktop is-half-touch">
<div className="interest-box">
<div className="interest-box-adjuster">
<div ref={"interest_box_content"} className={"interest-box-content " + (this.state.activated == true ? 'interest-box-activated' : '')} onClick={this.handleClick}>
<img className="interest-icon" src={this.images[this.props.icon]} style={{'height': '50%'}}></img>
<i className="activated-icon fas fa-check"></i>
<span ref={"interest_text"} className="interest-text">{this.props.title}</span>
</div>
</div>
</div>
</div>
)
}
}
export default InterestBox
In the InterestList "componentDidUpdate" method, the value of the item is an integer.
I want to use this integer to "click" on the InterestBox with the corresponding "id".
How can I achieve this ?
You can store an array of elements in one ref, like this:
constructor(props) {
super(props);
this.state = {pinterests: []}
this.pinterestRefs = React.createRef()
}
...
render() {
return (
<React.Fragment>
{Object.keys(this.state.pinterests).map((interest) => {
var pinterest = this.state.pinterests[interest];
return <InterestBox id={pinterest.id} onClick={this.props.onClick} icon={pinterest.picture_src} title={pinterest.name} ref={pinterestRef => this.refs.pinterestRefs.push(pinterestRef)} />
})}
</React.Fragment>
)
}
and then call the click function on each in a componentDidMount function:
componentDidMount() {
if (this.refs.pinterestRefs.length) {
this.refs.pinterestRefs.forEach(pinterestEl => {
pinterestEl.click();
});
}
}
Since this.pinterestRefs is a ref and not an array, the push method is not available. Unfortunately, we do not have a definite length so we can't declare the refs preemptively. However, we can add it to this.refs object and the convert it to an array:
export class InterestList extends Component {
constructor(props) {
super(props);
this.state = {pinterests: []}
}
componentDidMount() {
Axios.get('http://localhost:8000/api/interests')
.then((success) => {
this.setState({pinterests: success.data.data.interests});
})
}
componentDidUpdate(prevProps) {
console.log(Object.values(this.refs)); // Array with all refs
console.log(JSON.stringify(prevProps));
console.log(JSON.stringify(this.props))
if(this.props.alreadyChecked != prevProps.alreadyChecked) {
this.props.alreadyChecked.forEach((item) => {
console.log(item)
})
}
}
render() {
return (
{/*I'm assuming each item has a unique id, if not, create one*/}
<React.Fragment>
{Object.keys(this.state.pinterests).map((interest) => {
var pinterest = this.state.pinterests[interest];
return <InterestBox id={pinterest.id} onClick={this.props.onClick} ref={pinterest.id} icon={pinterest.picture_src} title={pinterest.name} />
})}
</React.Fragment>
)
}
}
export default InterestList;
i'm new in react and try to make a button that will remove the sibling class if i click in the current button, and also the current button can remove it self class (toggling),i already try and it seems im not find the clue. here my code below .
export default class Child extends Component {
render(){
return(
<button
onClick={this.props.onClick}
className={this.props.activeMode ? 'active' : ''}>
{this.props.text}
</button>
)
}
}
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
selectedIndex: null,
};
}
handleClick (selectedIndex) {
this.setState({
selectedIndex,
});
}
render () {
const array = ["Button 1","Button 2"];
return (
<div>
{array.map((obj, index) => {
const active = this.state.selectedIndex === index;
return <Child
text={obj}
activeMode={active}
key={index}
onClick={() => this.handleClick(index)} />
})}
</div>
)
}
}
I'm not shure that I understood your clearly right
Did you meen something like this?
class Child extends React.Component {
render(){
return(
<button
onClick={this.props.onClick}
className={this.props.activeMode ? 'active' : ''}>
{this.props.text}
</button>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedIndex: null,
};
}
handleClick (selectedIndex) {
let selected = this.state.selectedIndex === selectedIndex ? null : selectedIndex;
this.setState({
selectedIndex: selected,
});
}
render () {
const array = ["Button 1","Button 2"];
return (
<div>
{
array.map((obj, index) => {
return <Child text={obj}
activeMode={this.state.selectedIndex === index}
key={index}
onClick={() => this.handleClick(index)} />
})
}
</div>
)
}
}
ReactDOM.render(
<Parent name="World" />,
document.getElementById('container')
);
.active {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
class App extends Component {
constructor(props) {
super(props);
this.state = { Card: Card }
}
HandleEvent = (props) => {
this.SetState({Card: Card.Active}
}
render() {
return (
<Card Card = { this.state.Card } HandleEvent={
this.handleEvent }/>
<Card Card = { this.state.Card } HandleEvent={
this.handleEvent }/>
)
}
}
const Card = props => {
return (
<div style={props.state.Card} onClick={
props.HandleEvent}>Example</div>
)
}
Every time I click on one of the cards all of my elements change states, how do I program this to only change card that I clicked?
Here's a working example
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
0: false,
1: false
};
}
handleEvent(idx) {
const val = !this.state[idx];
this.setState({[idx]: val});
}
render() {
return (
<div>
<Card state={this.state[0]} handleEvent={()=>this.handleEvent(0) } />
<Card state={this.state[1]} handleEvent={()=>this.handleEvent(1) } />
</div>
);
}
}
const Card = (props) => {
return (<div onClick={() => props.handleEvent()}>state: {props.state.toString()}</div>);
}
You can also see it in action here
Obviously this is a contrived example, based on your code, in real world application you wouldn't store hardcoded state like {1: true, 2: false}, but it shows the concept
It's not completely clear from the example what is the Card in the constructor. But here the example of how you can modify clicked element.
Basically you can keep only index of clicked element in parent's state, and then pass it as some property to child component, i.e. isActive here:
const cards = [...arrayOfCards];
class App extends Component {
constructor(props) {
super(props);
this.state = { activeCardIndex: undefined }
}
HandleEvent = (index) => {
this.SetState({
activeCardIndex: index
});
}
render() {
return ({
// cards must be iterable
cards.map((card, index) => {
return (
<Card
key={index}
Card={Card}
isActive={i === this.state.activeCardIndex}
HandleEvent={this.HandleEvent.bind(this, index)}
/>
);
})
});
}
}
const Card = props => {
// style active card
const style = Object.assign({}, props.Card, {
backgroundColor: props.isActive ? 'orange' : 'white',
});
return (
<div style={style} onClick={
props.HandleEvent}>Example</div>
)
}