Using odd/even switch for classNames - javascript

I want each this.state.title to align according to a different classname.
I tried using css flex boxes/nth-of-type/nth-child, but it did not play nicely with React.
I'm using this.state to get my objects.
My unsuccessful attempt
render: function () {
let className
var newVar = !someVar;
switch(someVar) {
case odd:
className= "post-1 line";
break;
case even:
className = "post-2 right-align line";
break;
}
return (
<article class={I WANT THIS TO FILL FROM SWITCH}>
<div class="s-12 l-6 post-image">
<a href="post-1.html">
<img src="/post1.jpg">
</a>
</div>
<div class="s-12 l-5 post-text">
<a href="#">
<h2>{this.state.title}</h2>
</a>
<p>Testing
</p>
</div>
<div class="s-12 l-1 post-date">
<p class="date">28</p>
<p class="month">feb</p>
</div>
</article>
);
}
});

react rewrite class attribute as className,see react for more details.
render: function () {
let className = ['post-2 right-align line', 'post-1 line'][someVar % 2];
return (
<article className={className}>
<div className="s-12 l-6 post-image">
<a href="post-1.html">
<img src="/post1.jpg"/>
</a>
</div>
<div className="s-12 l-5 post-text">
<a href="#">
<h2>{this.state.title}</h2>
</a>
<p>Testing
</p>
</div>
<div className="s-12 l-1 post-date">
<p className="date">28</p>
<p className="month">feb</p>
</div>
</article>
);
}

The 'someVar' is local variable ? You can try write it in state, Via this.setState mutate it for your need.

here is the whole example like this:
class Item extends Component {
render() {
let {index, title}=this.props;
let className = ['post-2 right-align line', 'post-1 line'][index % 2];
return <li key={index} id={index} className={className}>{title}</li>
}
}
class TodoList extends Component {
render() {
let i = 0;
let itemFactory = (props = {}) => {
return <Item key={i} index={i++} {...props}/>
};
return (<ul>
{this.props.items.map((item) => itemFactory({title: item}))}
</ul>);
}
}
ReactDOM.render(
<TodoList items={['first', 'second', 'last']}/>,
document.getElementById('container')
);

Related

Reactjs handleclick binding in a nested map not working

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

hide a div in react when there is no anchor tag in it

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

Add +1 to willWatch when <a> is clicked

Task: add +1 to willWatch when <a> is clicked.
I have an error when <a> is clicked, because MovieItem is not a component. I try to set class MovieItem... but I have a problem with moviesData
import React, { Component } from "react";
import { moviesData } from "../moviesData";
function MovieItem(props) {
let {info : { id, vote_count , video, vote_average, title, popularity, poster_path, original_language, original_title ,backdrop_path, adult, overview} } = props;
return (
<div className="col" id={id} style={{width: "18rem"}}>
<img className="card-img-top" src={'https://image.tmdb.org/t/p/w500' + poster_path}
alt="Card image cap"/>
<div className="card-body">
<h5 className="card-title">Оригинальное название: {original_title}</h5>
<h5 className="card-title">Название: {title}</h5>
<p className="card-text">{overview}</p>
<p className="card-text">Рейтинг: {vote_average}</p>
<p className="card-text">Популярность: {popularity}</p>
<p className="card-text">Наличие видео: {video}</p>
<p className="card-text">Оригинальный язык: {original_language}</p>
<p className="card-text">Возраст 18+: {adult}</p>
<p className="card-text">backdrop_path {backdrop_path}</p>
<p className="card-text">Голоса: {vote_count}</p>
<a
// onClick={this.props.counter}
href="#"
className="btn btn-primary">Will Watch
</a>
</div>
</div>
)
}
class App extends Component {
constructor(state) {
super(state);
this.state = {
willWatch: 0
};
this.counter = this.counter.bind(this)
}
counter(e) {
e.preventDefault();
this.state.willWatch = this.state.willWatch + 1
}
render() {
return (
<div className="container">
<div className="col-12">
<div className="row">
<div className="col-9">
<div className="row">
{
moviesData.map((props) => {
return <MovieItem info={props} counter={this.counter}/>
})
}
</div>
</div>
<div className="col-3 sidebar">
<div className="row">
<p> Хочу посмотреть, фильмов: {this.state.willWatch} </p>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default App;
First of all, you have to destructure count from props like so:
function MovieItem(props) {
let {info : { id, vote_count , video, vote_average, title, popularity, poster_path, original_language, original_title ,backdrop_path, adult, overview}, counter } = props;
return (
<div className="col" id={id} style={{width: "18rem"}}>
<img className="card-img-top" src={'https://image.tmdb.org/t/p/w500' + poster_path}
alt="Card image cap"/>
<div className="card-body">
<h5 className="card-title">Оригинальное название: {original_title}</h5>
<h5 className="card-title">Название: {title}</h5>
<p className="card-text">{overview}</p>
<p className="card-text">Рейтинг: {vote_average}</p>
<p className="card-text">Популярность: {popularity}</p>
<p className="card-text">Наличие видео: {video}</p>
<p className="card-text">Оригинальный язык: {original_language}</p>
<p className="card-text">Возраст 18+: {adult}</p>
<p className="card-text">backdrop_path {backdrop_path}</p>
<p className="card-text">Голоса: {vote_count}</p>
<a
onClick={counter}
href="#"
className="btn btn-primary">Will Watch
</a>
</div>
</div>
)
}
and one more note, you have to set state using setState function:
counter(e) {
e.preventDefault();
this.setState((prevState) => {
return {
willWatch: state.willWatch + 1
}
});
}
You should never mutate/set this.state value directly.
Or else React would not know whether state has been changed or not.
(Refer to this article for details).
So instead of updating willWatch directly,
counter(e) {
e.preventDefault();
this.state.willWatch = this.state.willWatch + 1
}
Use setState
counter(e) {
e.preventDefault();
this.state(prevState => ({willWatch: prevState.willWatch + 1}));
}

Changing multiple props const to class

I want to convert functional component in React to class based component, because I want to make use of state property.
Currently I have this component which has 2 props.
const VideoListItem = ({video, onVideoSelect}) => {
const imageUrl=video.snippet.thumbnails.default.url;
return (
<li onClick={() => onVideoSelect(video)} className="list-group-item">
<div className="video-list media">
<div className="media-left">
<img className="media-object" src={imageUrl} />
</div>
<div className="media-body">
<div className="media-heading">{video.snippet.title}</div>
</div>
</div>
</li>
)
}
I tried to convert it to class and ended up with this:
class VideoListItem extends React.Component {
render() {
const {video} = this.props.video
const imageUrl=video.snippet.thumbnails.default.url;
const{onVideoSelect} =this.props.onVideoSelect
return (
<li onClick={() => onVideoSelect(video)} className="list-group-item">
<div className="video-list media">
<div className="media-left">
<img className="media-object" src={imageUrl} />
</div>
<div className="media-body">
<div className="media-heading">{video.snippet.title}</div>
</div>
</div>
</li>
)
}
}
However It does not work, I get errors. I guess it has something to do with 2 props being in the component. Anyone has idea how can I change the first code to be a class component?
Your destructuring is a bit off:
const { video } = this.props.video;
const{ onVideoSelect } = this.props.onVideoSelect;
should be something like:
const { video } = this.props;
const{ onVideoSelect } = this.props;
and you can combine these if you want:
const { video, onVideoSelect } = this.props;

attach onClick handler to populated list item reactjs

i have a function that i want to fire when a button is being clicked, this button is included in a list item that gets populated based on values from an array passed from the parent component, but its not working and returning an error saying that the function is undefined, how can i make this work?
import React from 'react';
export default class Card extends React.Component {
constructor(){
super()
this.state = {
index: null
};
}
handleClick(x){
this.setState({
index: x
})
}
render(){
/**
* Populates list items according to data passed
* on to resultsArray.
*/
var items = this.props.resultsArray;
var itemslist = items.map(function(item, index){
return(
<li key={ index } class="card" >
<div class="card-header">
<span class="hour-price"><span>{ item.hourPrice } € /hour</span></span>
<img src={ item.image } class="card-img" />
Book
</div>
<div>
<div class="card-info">
<p class="workplace-name">{ item.name }</p>
<span class="score">{ item.score } ★</span>
<p class="location">{ item.location }</p>
</div>
<div class="card-footer">
<p class="price">{ item.price } € / Day</p>
</div>
</div>
</li>
);})
return(
<div class="results-container">
<ul class="card-list center">
{ itemslist }
</ul>
</div>
);
}
}
You should change your render to the below codes. Use that instead of this.
var items = this.props.resultsArray;
var that = this;
var itemslist = items.map(function(item, index){
return(
<li key={ index } className="card" >
<div className="card-header">
<span className="hour-price"><span>{ item.hourPrice } € /hour</span></span>
<img src={ item.image } className="card-img" />
Book
</div>
......

Categories