Using react, I have a simple console.log('hello') in my app.js. Im passing it by props to a component, "thumbnails group" that will map over an array, create "thumbnails", and each one of them should fire that method with onClick. When I do so, nothing happens. What am I missing ?
App.js
escolheTextura = () => {
console.log('hello')
};
<ThumbnailGroup
escolheTextura={this.escolheTextura}/>
ThumbnailGroup.js
return (
<div className={["column", "group"].join(' ')}>
{
props.texturas.map(thumbnail => {
return <Thumbnail
onClick={() => props.escolheTextura()}>
{thumbnail}
</Thumbnail>
}
)
}
</div>
);
Thumbnail.js
return (
<div className={["button is-primary", "thumbnail"].join(' ')}>
<span>
<figure className={"image is-32x32"}>
<img src={"image is-32x32"}/>
</figure>
</span>
</div>
)
};
You should pass props.escolheTextura even further. DOM element receives the click, not a React.js component So, you have to pass your function down to Thumbnail component and make some DOM element, saying div, to handle click event:
ThumbnailGroup.js
return (
<div className={["column", "group"].join(' ')}>
{
props.texturas.map(thumbnail => {
return <Thumbnail
escolheTextura={props.escolheTextura}>
{thumbnail}
</Thumbnail>
}
)
}
</div>
);
ThumbnailGroup.js
return (
<div onClick={this.props.escolheTextura} className={["button is-primary", "thumbnail"].join(' ')}>
<span>
<figure className={"image is-32x32"}>
<img src={"image is-32x32"}/>
</figure>
</span>
</div>
)
ThumbnailGroup.js
return (
<div className={["column", "group"].join(' ')}>
{
props.texturas.map(thumbnail => {
return <Thumbnail
escolheTextura ={props.escolheTextura}>
{thumbnail}
</Thumbnail>
}
}
</div>
);
Thumbnail.js
return (
<div onClick={props.escolheTextura} className={["button is-primary", "thumbnail"].join(' ')}>
<span>
<figure className={"image is-32x32"}>
<img src={"image is-32x32"}/>
</figure>
</span>
</div>
)
};
Related
I have two files that return html fragments. They are identical except for the image. The fact is that the server has different paths to the playlist image and the genre image.
props.items.images[0].url
and
props.items.icons[0].url
because of this, I had to distribute this code to two different files. How could I combine them into one?
const Playlist = props => {
const playListClick = e => {
props.onClick(e.target.title);
}
return (
<section id={props.selectedValue} className="column column__hidden" onClick={playListClick}>
<a className="link__decoration link__track hover__track link__one" href="#">
<div>
{props.items.map((item, idx) =>
<div className="container" key={idx + 1} title={item.id} >
<div className="content__track" title={item.id}>
<img className="img__tarck" title={item.id} src={item.images[0].url}/>
<div className="name" title={item.id}>{item.name}</div>
</div>
</div>)}
</div>
</a>
</section>
);
}
const Genre = props => {
const genreClick = e => {
props.onClick(e.target.title);
}
return (
<section id={props.selectedValue} className="column column__hidden" onClick={genreClick}>
<a className="link__decoration link__track hover__track link__one" href="#">
<div>
{props.items.map((item, idx) =>
<div className="container" key={idx + 1} title={item.id}>
<div className="content__track" title={item.id}>
<img className="img__tarck" title={item.id} src={item.icons[0].url}/>
<div className="name" title={item.id}>{item.name}</div>
</div>
</div>)}
</div>
</a>
</section>
);
You can use a Conditional (ternary) Operator to look if icons exists and if not fallback to using the image. Further assistance is hard due to not knowing the surrounding circumstances.
<img className="img__tarck" title={item.id} src={item.icons ? item.icons[0].url : item.images[0].url}/>
I am writing a simple blog component in React. The component get the post from a second file and renders the content and the hashtags with map (in particular two nested map). I was trying to create a part in which the hashtag are highlighted when clicked, and hence I was following the snippet of the answer to this question. The fact is that it is not working and in particular what it is not working is the binding through bind (checked with console.log output)
class Blog extends React.Component {
state= {
open: {}
}
handleClick = (k) => {
let linkOpenState = true;
if (this.state.open.hasOwnProperty(k)) {
linkOpenState = !this.state.open[k];
}
this.setState({ open: { [k]: linkOpenState } })
}
render(){
return(
posts.map(i=>(
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span id={k} onClick={this.handleClick.bind(this,k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
)
}
}
export default Blog
Anyone has an idea of what is wrong there? Is it the nested map a problem for the bind?
React expects a single element as a return. You can solve this by wrapping the return in a fragment as <React.Fragment> or <>.
Also, you don't need to bind an arrow function(handleClick when mapping i.hash).
render(){
return(
<>
posts.map(i=>(
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span key={k} id={k} onClick={() => this.handleClick(k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
</>)}}
You don't need to bind the arrow function.
Also, you need to pass unique key to elements when used inside map, you can use index as a key but if you are mutating the array then use some id or hash or anything which will be unique.
class Blog extends React.Component {
state= {
open: {}
}
handleClick = (k) => {
let linkOpenState = false;
if (this.state.open.hasOwnProperty(k)) {
linkOpenState = this.state.open[k];
}
this.setState({ open: { [k]: linkOpenState } })
}
render(){
return(
posts.map((i, index)=>(
<div class="box" key={i.id || index}>
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span key={k} id={k} onClick={() => this.handleClick(k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
)
}
}
export default Blog
I have a react code (just a snippet, its not a complete code) as shown below which shows the list of programs on the webpage. Line A in the react code below renders all list of program on the webpage.
react code:
const renderPrograms = () => {
return programs.map((program, index)=>{
return (
<a href={program.url} key={index}>
<div className="program" >
<div class="hello-world">{program.name}</div>
</div >
</a>
)
})
}
return(
<div class="parent-div">
<div className ="pqr-xyz">
<h5>Hello World</h5>
</div>
<div className ="abc-def">
<h5>Programs</h5>
{programs && renderPrograms()} {/*Line A*/}
</div>
</div>
)
The above react code renders the following html code at runtime:
<div class="parent-div">
<div className ="pqr-xyz">
<h5>Hello World</h5>
</div>
<div class="abc-def">
<h5>Programs</h5>
<a href="https://www.google.com/">
<div class="program">
<div class="hello-world">TYUV</div>
</div>
</a>
<a href="https://www.twitter.com/">
<div class="program">
<div class="hello-world">SGHS</div>
</div>
</a>
</div>
</div>
Problem Statement:
When Line A does not render anything, my div (<div className ="abc-def">) will look like this at run time:
<div class="abc-def">
<h5>Programs</h5>
</div>
I am wondering what changes I need to make in my react code above so that when Line A doesn't render anything then <div class="abc-def"><h5>Programs</h5></div> should not display on the webpage.
They should be part of the condition:
return(
programs.length > 0 && <div className ="abc-def">
<h5>Programs</h5>
{renderPrograms()}
</div>
)
I changed the condition to check for length, otherwise you'll get a 0 instead of nothing, when empty
Try change this lines:
<div className ="abc-def">
<h5>Programs</h5>
{programs && renderPrograms()} {/*Line A*/}
</div>
into this:
{ programs && (
<div className ="abc-def">
<h5>Programs</h5>
{renderPrograms()} {/*Line A*/}
</div>
) }
Now without programs nothing is displayed.
You can try adding a className like this:
<div className={`abc-def ${programs.length ? "hidden" : ""}`}
Or from the parent componet, you can choose to not render this component with <div>
Instead of trying to hide it using CSS, you also can choose to not render it at all by returning nothing from your function
const RenderPrograms = ({ programs = [] }) => {
if (!programs.length) {
return []
}
return (
<div className="abc-def">
<h5>Programs</h5>
{programs.map((program, index) =>
<a href={program.url} key={index}>
<div className="program">
<div>{program.name}</div>
</div>
</a>
)}
</div>
)
}
ReactDOM.render(
<RenderPrograms />,
document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
You can try putting your class in a template string and using a ternary operator to display a certain class when some state value is true.
<div className={`${stateValue ? "name-of-class-with-hide" : "abc-def"}`}>
Here is a complete solution. All you need to do is move the <h5>Programs</h5> inside the condition and modify the condition slightly as shown in following snippet.
<div className ="abc-def">
{
programs.length > 0 && (
<div>
<h5>Programs</h5>
<RenderPrograms programs = {programs}/>
</div>
)
}
</div>
Note that I have modified the RenderPrograms function as well to accept arguments.
Following is a full snippet.
function RenderPrograms(props) {
return props.programs.map((program, index)=>{
return (
<a href={program.url} key={index}>
<div className="program" >
<div className="hello-world">{program.name}</div>
</div >
</a>
)
});
}
function MyApp() {
const programs=[
{name:'program 1', url: 'https://url1.com'},
{name:'program 2', url: 'url2'},
{name:'program 3', url: 'url3'},
];
const programs1 = []; // empty program list
return(
<div class="parent-div">
<h1>When programs list is not empty</h1>
{/*Above line is just for explanation -- you may remove it*/}
<div className ="pqr-xyz">
<h5>Hello World</h5>
</div>
<div className ="abc-def">
{
programs.length > 0 && (
<div>
<h5>Programs</h5>
<RenderPrograms programs = {programs}/>
</div>
)
}
</div>
{/*Below code is just for explanation -- you may remove it*/}
<hr />
<h1>When programs list is empty</h1>
<div className ="pqr-xyz">
<h5>Hello World</h5>
</div>
<div className ="abc-def">
{
programs1.length > 0 && (
<div>
<h5>Programs</h5>
<RenderPrograms programs = {programs1}/>
</div>
)
}
</div>
</div>
)
}
ReactDOM.render(
<MyApp />,
document.getElementById('app')
)
<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="app"></div>
You need to move rendering of programs into RenderPrograms component. Make RenderPrograms component purelu functional and pass data to it via props (Instead of using function).
const RenderPrograms = ({ programs }) => {
let disp = programs.map((program, index) => {
return (
<a href={program.url} key={index}>
<div className="program">
<div class="hello-world">{program.name}</div>
</div>
</a>
);
});
return disp || <span />;
};
then use RenderPrograms to render programs in main component (One with logis for loading programs).
// let programs = [
// {
// url: "test1.com",
// name: "test1"
// },
// {
// url: "test2.com",
// name: "test2"
// }
// ];
let programs = [];
return (
<div className="App">
<div class="parent-div">
<div className="pqr-xyz">
<h5>Hello World</h5>
</div>
<div className="abc-def">
<h5>Programs</h5>
<RenderPrograms programs={programs} />
{/* {programs && renderPrograms()} Line A */}
</div>
</div>
</div>
);
You can play around with this sandbox
I am using react version 16.13.1. And i have used hooks alot of time in my project.but now am getting this error even before i use it.
export default function TourData(props) {
const [collapsed, setCollapsed] = useState();
const withinDatacollapse = props.tourWithin.slice(1);
const withinDatashow = props.tourWithin.slice(0, 1);
function handleViewAll() {
setCollapsed(!collapsed);
}
function Object(props) {
return (
<div>
{props.data &&
props.data.map((item, index) => (
<div key={index} className="within">
<div
className="image"
style={{ background: `url(${item.image})` }}
>
<div className="destination">{item.country}</div>
</div>
<div className="options">
<div>
<h5>Options</h5>
<div className="user-select">
<Select
options={item.options}
placeholder={<h4>Select Option</h4>}
/>
</div>
</div>
</div>
<div className="date-select">
<div className="user-select">
<Select
options={item.date_options}
placeholder={<h4>Select Date</h4>}
/>
</div>
</div>
<div>
<button>Go!</button>
</div>
</div>
))}
</div>
);
}
return (
<div className="route">
<div className="list-box">
<Object data={withinDatashow} />
<Collapse in={collapsed}>
<Object data={withinDatacollapse} />
</Collapse>
<div className="bottom-view-section">
<button onClick={handleViewAll}>View All</button>
</div>
</div>
</div>
);
}
Here i just declared the hook, and am getting this error.
I cant find where i have went wrong.
At first i had an error TypeError: Cannot read property 'map' of undefined where i do mapping , so i had to put props.data && just before the mapping.
You shouldn't name your components as Javascript's keywords like Object or class.
Also, you might want to define default value for props.placeData so when it is undefined, you don't get errors.
export default function AppMain({placeData = [], ...props}) {
Or you can just check in return, and say something like 'there is no place'. It's up to you.
I have an image gallery where I loop trough image objects and I want to pass the i to my onClick function. This is my image gallery code:
<div className="gallery clearfix">
{ block.gallery.map((item, i) => (
i < 1 ?
<div className="gallery-image" key={i} onClick={this.toggle}>
<a href='' className="inner">
<img src={item.images.thumbnail_sm} alt={block.title} srcSet={`${item.images.thumbnail_md} 1x, ${item.images.thumbnail_lg} 2x`} className="img-fluid image"/>
</a>
</div>
: null
))}
<div className="gallery-thumbs">
<div className="row">
{ block.gallery.map((item, i) => (
i > 0 && i < (limit + 1) ?
<div className="gallery-item" key={i} onClick={this.toggle}>
<a href='' className="inner">
<img src={item.images.thumbnail_sm} alt={block.title} srcSet={`${item.images.thumbnail_md} 1x, ${item.images.thumbnail_lg} 2x`} className="img-fluid image" title="" />
{ block.gallery.length > (limit + 1) && i == limit ?
<div className="img-overlay">
<span className="img-indicator">{ block.gallery.length - (limit + 1) }+ <span className="hidden-xs">Foto's</span></span>
</div>
: null
}
</a>
</div>
: null
))}
</div>
</div>
</div>
And this is my reactstrap modal where I want to show the image which is clicked:
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<ModalBody>
<img src={block.gallery[this.state.clickedImage].item.images.thumbnail_lg}/>
</ModalBody>
</Modal>
And here is the toggle function where I want to pass the clickedImage id:
toggle(id) {
this.setState({
clickedImage: id,
modal: !this.state.modal
});
}
For best practice, I don't suggest binding within onClick, that cause it invoke bind every time when it's clicked. if you are using ES6, instead you should bind it in constructor:
Class MyComponent extends React.Component {
constructor(props){
super(props);
this.toggle = this.toggle.bind(this);
}
}
and
<div className="gallery-item" key={i} onClick={(i) => this.toggle(i)}></div>
UPDATE: like comments say. this is actually is not the best way, the best way is not to create new function and attach events with every render, which means it should be just
<div className="gallery-item" key={i} onClick={this.toggle}></div>
but since you need to pass the id, the best bet would be refactor it into smaller components like <GalleryItem> and pass the id by props
Worth to read: this
UPDATE: Also please look at comments, using dataset.index and data-index is even better