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;
Related
I use map but I have one problem I am trying to set the background of a certain element inside the map, in my case it is "item .title" I want my element to look like this https://ibb.co/ccJzD6g as you can see in the picture background color " 100% "which I specifically set in the console for but in fact if I set" background: orange "for the sidebar_list class, then the color will be applied to all elements including the rest , https://ibb.co/ZBTX3hd and if I set the background for the titleName class it looks like this https://ibb.co/84RKBBw but I want it to look like this https://ibb.co/ccJzD6g how do i solve this problem?
Lesson.jsx
import React from 'react';
import less from "./css/lesson.module.css";
import "./css/betaLesson.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { Navbar } from "../../../Navbar/Navbar";
export class Lessons extends React.Component {
constructor(props) {
super(props);
this.state = {
indexDescription: 0,
}
}
render() {
const listLessons = this.props.lesson.map((item, index) => {
return (
<li key={index} className={less.sidebar_list} onClick={() => {
this.setState({ indexDescription: index })
}}>
<div>
<FontAwesomeIcon className={less.item_icon} icon={item.iconName} />
</div>
<div style={{background: "orange"}} className={less.titleName}>
<p>{item.title}</p>
</div>
<div className={less.titleName}>
<p>{item.titleName}</p>
</div>
</li>
);
});
return (
<>
<div className="abc">
<Navbar color="blue" bg="tomato" centerFlexNavbarContainer="flex"
navbarSearchPage="Search" navbarHomePage="Home" centerHeadlineNavbarColumn="center" />
<div className={less.wrapper}>
<div>
<div className={less.sidebar}>
<div>
<ul>
{listLessons}
</ul>
</div>
</div>
</div>
<div className={less.main_content}>
<div className={less.main_inside_content}>
<div className={less.header}>
<div>
<h2>JavaScript JSON Reference</h2>
</div>
</div>
<div className={less.info}>
<div className={less.description}>
<p>
{
this.props.lesson[this.state.indexDescription]["description"]
}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}
}
First question answer: Create a new component with its own attributes separately and map/passdown the properties to the component.
Here's an example: https://codesandbox.io/s/objective-hopper-2st8g?file=/src/Lesson.js
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}>
EDIT: [SOLVED by the community - in the answers!] Thanks everyone
I'm trying to add search functionalities to my webApp (a list of the movies I own). To do this, from the app I'm calling a functional component (MovieListFiltered) which has the following code:
MovieListFiltered.js:
import React from 'react'
const MovieListFiltered = (props) => {
const newData = props.moviesAfterFilter
if(newData !== null) {
const newMovies = newData.map((movie, i) =>
{
return(
<div className="col s12 m3 l3" key={i} movieid ={movie.idFromTmdb}>
<div className="card">
<div className="card-image waves-effect waves-block waves-light">
<img src={movie.url2poster} alt={movie.movieTitle} className="responsive-img" />
<p className="littleFont" align="center"><span><b>{movie.movieTitle}</b></span></p>
</div>
<div className="card-action">
<a href="#" onClick={() => this.props.viewMovieInfo(movie.idFromTmdb)}>Movie Details</a>
</div>
</div>
</div>
);
})
console.log(newMovies)
props.movieCallback(newData, newMovies);
} else {
return null
}
}
export default MovieListFiltered
So, basically, notything special there: you see many console.log calls, that was just to make sure the correct array of data was passed (and it is!)
In App.js:
... code not interesting goes here ...
callbackFromList = (childDataData, childDataMovies) => {
this.setState({moviesToFilter: childDataData});
this.setState({moviesToShow: childDataMovies});
this.setState({totalResults: childDataData.length});
}
render()
{
... some not interesting code goes here...
return(
<div className="App">
<Nav />
<div>
<div className="container">
<div className="row">
<div className="col s10 offset-s1">
<MovieListFiltered viewMovieInfo={this.viewMovieInfo} movieCallback={() => this.callbackFromList} ref={this.movieListRef} moviesAfterFilter={this.state.moviesFiltered}></MovieListFiltered>
</div>
</div>
</div>
</div>
</div>
);
}
Can you please help me? I've read all the questions already made here on stackoverflow, but nothing seems to fit to my case.
I think you are wanting something like this:
const MovieListFiltered = (props) => {
const newData = props.moviesAfterFilter
if(newData !== null) {
const newMovies = newData.map((movie, i) => (
<div className="col s12 m3 l3" key={i} movieid ={movie.idFromTmdb}>
<div className="card">
<div className="card-image waves-effect waves-block waves-light">
<img src={movie.url2poster} alt={movie.movieTitle}
className="responsive-img" />
<p className="littleFont" align="center"><span><b>
{movie.movieTitle}</b></span></p>
</div>
<div className="card-action">
<a href="#" onClick={() =>
this.props.viewMovieInfo(movie.idFromTmdb)}>Movie Details</a>
</div>
</div>
</div>
);
)
console.log(newMovies)
props.movieCallback(newData, newMovies)
return newMovies
} else {
return null
}
}
Here a cleaner version with only one return. That may not be what you're looking for though.
import React from 'react'
const MovieListFiltered = (props) => {
const newData = props.moviesAfterFilter || []; // add 'or' if null or undefined
const newMovies = newData.map((movie, i) => (
<div className="col s12 m3 l3" key={i} movieid ={movie.idFromTmdb}>
<div className="card">
<div className="card-image waves-effect waves-block waves-light">
<img src={movie.url2poster} alt={movie.movieTitle} className="responsive-img" />
<p className="littleFont" align="center"><span><b>{movie.movieTitle}</b></span></p>
</div>
<div className="card-action">
<a href="#" onClick={() => this.props.viewMovieInfo(movie.idFromTmdb)}>Movie Details</a>
</div>
</div>
</div>
));
console.log(newMovies)
props.movieCallback(newData, newMovies);
return newMovies;
}
export default MovieListFiltered
You are only returning a value from the else block. The if block is not currently returning anything. You probably want to make the last line of the if block return newMovies;
import React from 'react'
const MovieListFiltered = (props) => {
const newData = props.moviesAfterFilter
if(newData !== null) {
const newMovies = newData.map((movie, i) =>
{
return(
<div className="col s12 m3 l3" key={i} movieid ={movie.idFromTmdb}>
<div className="card">
<div className="card-image waves-effect waves-block waves-light">
<img src={movie.url2poster} alt={movie.movieTitle} className="responsive-img" />
<p className="littleFont" align="center"><span><b>{movie.movieTitle}</b></span></p>
</div>
<div className="card-action">
<a href="#" onClick={() => this.props.viewMovieInfo(movie.idFromTmdb)}>Movie Details</a>
</div>
</div>
</div>
);
});
console.log(newMovies);
props.movieCallback(newData, newMovies);
return newMovies;
}
return null;
}
export default MovieListFiltered
Also, you might notice I got rid of the entire else block - this is because it's not necessary if you return from the corresponding if block.
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;
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')
);