Hi ! React issues i need assistance with event handlers - javascript

I'm trying to make a components props(sideBarInfo) details show up on the left column of a Page after clicking on a corresponding component(thumbnail) on the right column of the same Page.
Please note that all imports and exports are used in the main project(i removed them here).
I've also imported all components into the main (Page.js). Yet i keep getting a Type error for the onClick.
This is the first component - Thumbnail
class Thumbnail extends React.Component {
render(){
return (
<div className="Work" onClick={(e) => this.props.click(this.props.work)} >
<div className="image-container">
<img src={this.props.work.imageSrc} alt={this.props.work.imageSrc}/>
</div>
<div className="Work-information">
<p> {this.props.work.work}</p>
</div>
</div>
);
} }
This is the ThumbnailList
class ThumbnailList extends React.Component {
constructor(props){
super(props);
this.state= {
works: [
{
id: 0,
work: 'Work 1',
imageSrc: W1,
view: '#',
selected: false
},
{
id: 1,
work: 'Work 2',
imageSrc: W2,
view: '#',
selected: false
},
]
}
}
handleCardClick = (id,card) => {
console.log(id);
let works= [...this.state.works];
works[id].selected = works[id].selected ? false : true ;
works.forEach(work=>{
if(work.id !== id){
work.selected = false;
}
});
this.setState({
works
})
}
makeWorks = (works) => {
return works.map(work => {
return <Thumbnail work={work} click={(e => this.handleCardClick(work.id,e ))} key={work.id} />
})
}
render(){
return(
<div>
<div className="scrolling-wrapper-flexbox">
{this.makeWorks(this.state.works)}
</div>
</div>
);
}
}
This is the sidebarInfo
function SidebarInfo(props) {
return (
<img width="370" height="370" src= {props.imageSrc} />
<p> {props.work} </p>
);}
This is the problematic Page - the boldened keeps giving a Type error(cannot read property 'selected' of undefined.)
class Page extends React.Component {
render() {
return (
<span>
<div className="column left">
<div className="">
**{this.props.work.selected && <SidebarInfo imageSrc={this.props.imageSrc} /> }**
</div>
</div>
<div className="column right" >
<div>
<ThumbnailList />
</div>
</div>
</span>
)
}
}

You may need to add a check in this.props.work.selected like this this.props.work && this.props.work.selected
To ensure that this.props.work is defined before checking on this.props.work.selected

I was able to answer this for anyone interested.
there were some foundational errors in my file arrangement which have been corrected.
After refactoring, i was able to achieve what i wanted using 3 classes/functions in; App.js, Thumbnail.js and SideBarInfo.js
see one working solution with styling on this sandbox: https://codesandbox.io/s/modern-sky-y9d3c
See Solution below;
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
works: [
{
id: 0,
work: "Work 1",
imageSrc:
"https://images.unsplash.com/photo-1584608168573-b6eec7a04fd7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=701&q=80",
view: "#",
selected: false
},
{
id: 1,
work: "Work 2",
imageSrc:
"https://images.unsplash.com/photo-1581665269479-57504728e479?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=701&q=80",
view: "#",
selected: false
}
]
};
this.handleCardClick = this.handleCardClick.bind(this);
this.makeWorks = this.makeWorks.bind(this);
this.showWorks = this.showWorks.bind(this);
}
handleCardClick = (id, Thumbnail) => {
console.log(id);
let works = [...this.state.works];
works[id].selected = works[id].selected ? false : true;
works.forEach((work) => {
if (work.id !== id) {
work.selected = false;
}
});
this.setState({
works
});
};
makeWorks = (works) => {
return works.map((work) => {
return (
<Thumbnail
work={work}
click={(e) => this.handleCardClick(work.id, e)}
key={work.id}
/>
);
});
};
showWorks = (works) => {
let i = 0;
var w = [];
while (i < works.length) {
if (works[i].selected) {
w = works[i];
}
i++;
}
return ( <SidebarInfo imageSrc={w.imageSrc} work={w.work} /> );
};
render() {
return (
<div className="App">
<span>
<div> { this.showWorks(this.state.works)} </div>
<div className="">
<div className="scrolling-wrapper-flexbox">
{this.makeWorks(this.state.works)}
</div>
</div>
</span>
</div>
);
}
}
class Thumbnail extends React.Component {
render() {
return (
<div>
<div className="column left">
{/* this.props.work.selected && <SidebarInfo work={this.props.work.work} imageSrc={this.props.work.imageSrc}/> */}
</div>
<div className="column right">
<div className="Work" onClick={(e) => this.props.click(this.props.work)}>
<div className="image-container">
<img src={this.props.work.imageSrc} alt={this.props.work.imageSrc} />
</div>
<div className="Work-information">
<p> {this.props.work.work}</p>
</div>
</div>
</div>
</div>
);
}
}
export default Thumbnail;
function SidebarInfo(props) {
return (
<div className="one">
<div className="Work">
<h1> NAME </h1>
<div className="image-container">
<img
width="370"
height="370"
src={props.imageSrc}
alt={props.imageSrc}
/>
</div>
<p> {props.work} </p>
<p> {props.view} </p>
</div>
</div>
);
}
export default SidebarInfo;

Related

how fix selected permanently ReactJS

I would like to explain my problem of the day.
the code works correctly ,
the only problem is
when i click on my navbar to select a tab ,
my activeclassname works fine except that when i click elsewhere on the same page i lose my activeclassname
I would like him to be active permanently
Do you have an idea of how to fix this?
class MainHome extends Component {
constructor(props) {
super(props);
this.state = {
};
// preserve the initial state in a new object
this.toggle = this.toggle.bind(this);
this.cgtChange1= this.cgtChange1.bind(this);
this.cgtChange2= this.cgtChange2.bind(this);
}
cgtChange1() {
console.log('bière clicked')
this.setState({
cgt : 1
})
}
cgtChange2() {
console.log('cocktails clicked')
this.setState({
cgt :2
})
}
render() {
let addedItems = this.props.items.length ?
(
this.props.items.filter(item => item.ctg === this.state.cgt).map(item => {
return (
<div key={item.id}>
{item.title}
</div>
)
})) :
(
<div></div>
)
return (
<div>
<Header text='Carte'/>
<NavBar
onClick1={this.cgtChange1}
onClick2={this.cgtChange2}
/>
{addedItems}
</div>
)
}
}
NavBar
export default class FocusOnSelect extends Component {
render() {
return (
<div>
<button onClick={this.props.onClick1}
activeClasseName="selected" className='inactive' > Bières
</button>
</div>
<div >
<button onClick={this.props.onClick2} activeClasseName="selected" className='inactive' > Cocktails </button>
</div>
</div>
);
}
}

How to put classname and variables in ternary at Reactjs?

I have different datasets. Some of them have only icon and some of them have images. I want to control the data whether iconurl is null or not in the beginning and then if has icon url I will use FontAwesome if iconurl = null I will use img tag. But my images does not come to screen. Also I have could not bring the name to h3 tag. It looks empty on the screen. I am confused a bit. May you help me please? thank you in advance.
Note: I have already checked some ternary examples in react but they were too basic.
render() {
return (
this.state.diagnoses.map(user =>
<div className={'cat-box-region'}>
<div className={'cat-box-position'}>
<div className={'cat-box'}>
{user.IconUrl ? (<FontAwesomeIcon icon={user.IconUrl} size="3x" className={'icon'}/>) : (<img src={user.ImgUrl} className={'ultrasound-img'}/>)}
<h3>{user.name}</h3>
</div>
</div>
</div>
)
);
}
I solved problem like that :
class DiagnosisBox extends Component {
static propTypes = {};
state = {
diagnoses: [],
diagnosesWithImg: [],
diagnosesWithIcon: []
};
componentDidMount() {
fetch('http://localhost:51210/api/service')
.then(response => {
return response.json();
}).then(diagnoses => {
this.setState({
diagnoses,
});
console.log(diagnoses);
})
}
render() {
this.state.diagnoses.map(stateData =>
stateData.iconUrl ? this.state.diagnosesWithIcon.push(stateData)
: this.state.diagnosesWithImg.push(stateData)
);
return (
<div className={'cat-box-region'}>
{this.state.diagnosesWithIcon.map(data =>
<div key={data.id} className={'cat-box-position'}>
<div className={'cat-box'}>
<FontAwesomeIcon icon={data.iconUrl} size="3x" className={'icon'} />
<h3>{data.diagnosisName}</h3>
</div>
</div>
)}
{this.state.diagnosesWithImg.map(data =>
<div key={data.id} className={'cat-box-position'}>
<div className={'cat-box'}>
<img src={data.ImgUrl} className={'ultrasound-img'}/>
<h3>{data.diagnosisName}</h3>
</div>
</div>
)}
</div>
);
}
}
export default DiagnosisBox;

react expand and collapse just one panel

Need help with react...
Trying to implement a collapsible list of cards with weather information.
Already implemented the behavior of expand and collapse, but when i clicked on one panel the other panel open at the same time (i have 2 panels and need 7 to display weahter for 7 days of the week).
How can i open and close just one panel?
Code:
import React, { Component } from 'react';
import Moment from 'react-moment';
import RandomGif from './RandomGif.js';
const urlForCity = city => `https://cors-anywhere.herokuapp.com/http://api.openweathermap.org/data/2.5/forecast/daily?q=${city}&units=metric&cnt=7&appid=1fba7c3eaa869008374898c6a606fe3e`
class OpenWapi extends Component {
constructor(props) {
super(props);
this.state = {
requestFailed: false,
shown: false
}
this.componentDidMount = this.componentDidMount.bind(this);
this.toggle = this.toggle.bind(this);
}
componentDidMount() {
fetch(urlForCity(this.props.city))
.then(response => {
if(!response.ok) {
throw Error("Network request failed")
}
return response;
})
.then(data => data.json())
.then(data => {
this.setState({
weatherData: data
})
}, () => {
this.setState({
requestFailed: true
})
})
}
toggle() {
this.setState({
shown: !this.state.shown
});
}
render() {
if(this.state.requestFailed) return <p>Request Failed.</p>;
if(!this.state.weatherData) return <p>Loading...</p>;
return (
<div>
<p>City: {this.state.weatherData.city.name}</p>
{/* Day 1 */}
<div onClick={this.toggle} className="dayWeekItem">
<div className="top-content">
<div className="icon-weather"></div>
<div className="date">
<div className="weekday">Today</div>
<div className="day-long"><Moment unix format="MMM DD YYYY">{this.state.weatherData.list[0].dt}</Moment></div>
</div>
<div className="temperature">
<div className="temp-high">{parseInt(this.state.weatherData.list[0].temp.max)}º</div>
<div className="temp-low">{parseInt(this.state.weatherData.list[0].temp.min)}º</div>
</div>
</div>
<div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
<div className="weather-gif" >
<RandomGif keyword={this.state.weatherData.list[0].weather[0].description} />
</div>
</div>
</div>
{/* Day 2 */}
<div onClick={this.toggle} className="dayWeekItem">
<div className="top-content">
<div className="icon-weather"></div>
<div className="date">
<div className="weekday">Tomorrow</div>
<div className="day-long"><Moment unix format="MMM DD YYYY">{this.state.weatherData.list[1].dt}</Moment></div>
</div>
<div className="temperature">
<div className="temp-high">{parseInt(this.state.weatherData.list[1].temp.max)}º</div>
<div className="temp-low">{parseInt(this.state.weatherData.list[1].temp.min)}º</div>
</div>
</div>
<div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
<div className="weather-gif" >
<RandomGif keyword={this.state.weatherData.list[1].weather[0].description} />
</div>
</div>
</div>
{/* Day 3 */}
{/* Day 4 */}
{/* Day 5 */}
</div>
)
}
}
export default OpenWapi;
I would have an object to represent the state, a field for each panel.
Like this:
constructor(props) {
...
this.state = {
requestFailed: false,
shown: {}
}
...
}
...
toggle(panelNumber) {
this.setState({
shown: {
...this.state.shown,
[panelNumber]: !this.state.shown[panelNumber]
}
});
}
...
The toogle function is used like this, for instance, Day 1:
<div onClick={() => this.toggle(1)} className="dayWeekItem">
...
</div>
And to show in html, for instance, Day 1:
<div className={this.state.shown[1] ? "toggleContent-open" : "toggleContent-closed"} >
<div className="weather-gif" >
<RandomGif keyword={this.state.weatherData.list[0].weather[0].description} />
</div>
</div>
They all will collapse always with your implementation.
You have a state
state = {
shown: true
}
You have a function to toggle it
toggle = () => {
this.setState(shown: !this.state.shown)
}
And you render the component, using the this.state.shown in two places, but the value will always be one true or false
render() {
return(<div .....//something>
<div onClick={this.toggle}>
{ this.state.shown ? <SomeComponent or HTML Tag> : null }
</div>
<div onClick={this.toggle}>
{ this.state.shown ? <SomeComponent or HTML Tag> : null }
</div>
</div>)
}
So where ever you toggle, once the state is updated and render method is called again to paint the view, both sections of divs get the sameBoolean` value. Therefore, they both collapse.
Best Solution I can offer for this problem will be:
Create a separate component which has two jobs to be do:
1. Maintains its own state, of collapse true or false.
2. Render the children given to it without wondering what they might be.
So let say
class WeatherWidget extends React.PureComponent {
state= {
shown: true
}
toggle = () => this.setState({shown: !this.state.shown})
render() {
return(
<div onClick={this.toggle} className="dayWeekItem">
<div className="top-content">
<div className="icon-weather"></div>
<div className="date">
<div className="weekday">Today</div>
<div className="day-long">
<Moment unix format="MMM DD YYYY">{this.props.date}</Moment>
</div>
</div>
<div className="temperature">
<div className="temp-high">{parseInt(this.props.maxTemp)}º
</div>
<div className="temp-low">{parseInt(this.props.minTemp)}º
</div>
</div>
</div>
<div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
<div className="weather-gif" >
<RandomGif keyword={this.props.gifDescription} />
</div>
</div>
</div>
)
}
}
So you create a reusable component which manages its own state ( React Paradigm/ Composition brings reusability)
As for displaying multiple widgets
class OpenWapi extends Component {
constructor(props) {
super(props);
this.state = {
requestFailed: false,
shown: false
}
this.componentDidMount = this.componentDidMount.bind(this);
this.toggle = this.toggle.bind(this);
}
componentDidMount() {
fetch(urlForCity(this.props.city))
.then(response => {
if(!response.ok) {
throw Error("Network request failed")
}
return response;
})
.then(data => data.json())
.then(data => {
this.setState({
weatherData: data
})
}, () => {
this.setState({
requestFailed: true
})
})
}
render() {
if(this.state.requestFailed) return <p>Request Failed.</p>;
if(!this.state.weatherData) return <p>Loading...</p>;
return(
<div>
<p>City: {this.state.weatherData.city.name}</p>
<WeatherWidget
date={this.state.weatherData.list[0].dt}
maxTemp={this.state.weatherData.list[0].temp.max}
minTemp={this.state.weatherData.list[0].temp.min}
gifDescription=
{this.state.weatherData.list[0].weather[1].description}
/>
<WeatherWidget
date={this.state.weatherData.list[1].dt}
maxTemp={this.state.weatherData.list[1].temp.max}
minTemp={this.state.weatherData.list[1].temp.min}
gifDescription=
{this.state.weatherData.list[1].weather[1].description}
/>
</div>
)
}
Hopefully, this solves the use case.

this.someFunction is not a function

After having read about the bind requirement for methods to be bound to a React ES6 class, I am still having some difficulty with this example:
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = { products: [] };
this.updateState = this.updateState.bind(this);
}
componentDidMount() {
this.updateState();
}
handleProductUpvote(productId) {
Data.forEach((item) => {
if (item.id === productId) {
item.votes = item.votes + 1;
return;
}
});
this.updateState();
}
updateState() {
const products = Data.sort((a,b) => {
return b.votes - a.votes;
});
this.setState({ products });
}
render() {
const products = this.state.products.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>
);
}
}
class Product extends React.Component {
constructor() {
super();
this.handleUpvote = this.handleUpvote.bind(this);
}
handleUpvote() {
this.props.onVote(this.props.id);
}
render() {
return (
<div className='item'>
<div className='image'>
<img src={this.props.product_image_url} />
</div>
<div className='middle aligned content'>
<div className='header'>
<a onClick={this.handleUpvote}>
<i className='large caret up icon'></i>
</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>
);
}
}
ReactDOM.render(
<ProductList />,
document.getElementById('content')
);
This returns
Uncaught TypeError: this.updateState is not a function(...) at handleProductUpvote
Is the initialized binding not sufficient in this case?
Whenever you see this issue, you don't want to be adding the bind to the method that it's trying to call right then, but the method that you are inside of when the "this.xxx not defined" issue occurs.
Currently, it's getting the function handleProductUpvote just fine - but it's calling it in the wrong object context. So you need to do the same thing as you did with updateState in the constructor, but with that function. Though I have limited react knowledge I believe it's common to do that for every function that's used as an event listener or callback.

react show button on mouse enter

I have a react component which hold method like:
mouseEnter(){
console.log("this is mouse enter")
}
render(){
var album_list;
const {albums} = this.props
if(albums.user_info){
album_list = albums.user_info.albums.data.filter(album => album.photos).map((album => {
return
<div className={"col-sm-3"} key={album.id} onMouseEnter={this.mouseEnter}>
<div className={(this.state.id === album.id) ? 'panel panel-default active-album' : 'panel panel-default'} key={album.id} onClick={this.handleClick.bind(this, album.id)}>
<div className={"panel-heading"}>{ album.name }</div>
<div className={"panel-body"}>
<img className={"img-responsive center-block"} src={album.photos.data[0].source} />
</div>
</div>
</div>
}))
}
return (
<div className={"container"}>
<div className="row">
{album_list}
</div>
</div>
)
}
}
Here I have onMouseEnter on album_list. When it is hover or mouse enter I want to dispalay a button on that div.
How can I do that ??
Thank you
Update the component's state to reflect whether the mouse is inside the component, then use the state value to conditionally render a button.
getInitialState() {
return {
isMouseInside: false
};
}
mouseEnter = () => {
this.setState({ isMouseInside: true });
}
mouseLeave = () => {
this.setState({ isMouseInside: false });
}
render() {
return (
<div onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave}>
{this.state.isMouseInside ? <button>Your Button</button> : null}
</div>
);
}
Inside the render function we use the conditional operator (?) to return the button component if this.state.isMouseInside is truthy.
There is another approach that uses a reusable render component that would make components 'hoverable' or 'revealable' - whatever makes sense.
class Hoverable extends Component {
constructor() {
super();
this.state = {
isMouseInside: false
};
}
mouseEnter = () => {
this.setState({ isMouseInside: true });
}
mouseLeave = () => {
this.setState({ isMouseInside: false });
}
render() {
return this.props.children(
this.state.isMouseInside,
this.mouseEnter,
this.mouseLeave
)
}
}
Then create the functional component that represents the hoverable element. E.g an album
const HoverableElement = props => (
<Hoverable>
{(isMouseInside, mouseEnter, mouseLeave) => (
<div className="menu-item">
<div onMouseEnter={mouseEnter} onMouseLeave={mouseLeave}>
<h2>{props.title}</h2>
</div>
{isMouseInside && props.children}
</div>
)}
</Hoverable>
)
Finally, use the HoverableElement to render a list of elements that will each be 'hoverable' e.g an array of albums
class HoverableElementsList extends Component {
render() {
return (
<div>
<HoverableElement title="First Menu">
<p>Some children content</p>
</HoverableElement>
</div>
)
}
}

Categories

Resources