Child Not Re-rendering on List Item Deletion in Parent but Adding to the List Item Triggering Re-render - reactjs

I have Simple Add and Delete to my List sample ..
I made two child components
Lead Form Component ( Which Add New Leads to the List )
Lead List Component ( Which Simply render Leads List also have delete button which trigger delete action by passing ID back to parent )
In parent , the.state.leads holds all the leads
on Form Submit .. it adds to the.state.leads and LEAD LIST CHILD Components
successfully Re-Renders with new added lead
but on deleting list in the LEAD LIST , The lead list not re renders
Image ; Dev Tool Debug in the browser -React Console screenshot ..
MY LeadList Component
.........................................................
class LeadList extends React.Component {
constructor(props) {
super(props);
this.state = {
leads: this.props.avlList
};
this.handelDeleteLead = this.handelDeleteLead.bind(this);
}
handelDeleteLead(e) {
e.preventDefault();
this.props.DeleteLead(e.target.id);
}
render() {
console.log(this.state.leads);
return (
<div>
<ul>
{this.state.leads.map(item => (
<li key={item.id}>
{item.name} - {item.mobile} -{item.active ? "Active" : "Inactive"}
-
<div
id={item.id}
onClick={this.handelDeleteLead}
cursor="pointer"
>
X
</div>
</li>
))}
</ul>
</div>
);
}
}
......
My APP.js Parent Componnet
....................................
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
leads: [{ id: 1, name: "Panindra", mobile: "88842555542", active: true }]
};
this.handleAddToLeads = this.handleAddToLeads.bind(this);
this.handleRemoveLeads = this.handleRemoveLeads.bind(this);
}
handleAddToLeads(lead) {
let newleadsTemp = this.state.leads;
lead.id = Math.random() * Math.random();
newleadsTemp.push(lead);
// assign a name of list to item list
let newLeads = newleadsTemp;
this.setState({
leads: newLeads
});
}
handleRemoveLeads(lead_id) {
console.log(" Leads list before fitler ..." + this.state.leads);
let newFitleredLeads = remove(this.state.leads, lead_id);
this.setState({
leads: newFitleredLeads
});
console.log(" Leads list after fitler ..." + this.state.leads);
}
render() {
return (
<div className="App">
<h1> My First Redux</h1>
<hr />
<div className="leadList">
<LeadList
avlList={this.state.leads}
DeleteLead={this.handleRemoveLeads}
/>
</div>
<div className="leadForm">
<LeadForm NewLead={this.handleAddToLeads} />
</div>
</div>
);
}
}
.....

I think the problem is that you use state in LeadList component. Try to remove state from LeadList component. You don't need to manage multiple state's (this is important).
class LeadList extends React.Component {
render() {
return (
<div>
<ul>
{this.props.avlList.map(item => (
<li key={item.id}>
{item.name} - {item.mobile} -{item.active ? "Active" : "Inactive"}
-
<div
id={item.id}
onClick={() => this.props.DeleteLead(item.id)}
cursor="pointer"
>
X
</div>
</li>
))}
</ul>
</div>
);
}
}
And fix handleRemoveLeads function in the parent (App) component.
handleRemoveLeads(lead_id) {
console.log(" Leads list before fitler ..." + this.state.leads);
// THIS IS NOT WORKING
//let newFitleredLeads = remove(this.state.leads, lead_id);
// BETTER SOLUTION
let newFitleredLeads = this.state.leads.filter(item => item.id !== lead_id);
this.setState({
leads: newFitleredLeads
});
console.log(" Leads list after fitler ..." + this.state.leads);
}
This should work fine.
Working example (without form): https://codesandbox.io/s/charming-kowalevski-rj5nj

Related

How to pass an user-created array to another component?

I am new to React. In fact I am new to any frontend programming lanugage. Therefore I encounter many really weird and sometimes even hilarious problems. I am struggling with sending an array to another compontent. The problem is user creates that array, and it's created dynamically inside render(){return(..)}
class Home extends Component {
constructor(props) {
super(props)
this.chosenItems = [];
}
state = {
items: [],
};
// code to import JSON from backend API, irrelevant, that part works fine
addItem(item){
this.chosenItems.push(item);
console.log(this.chosenItems); //logs created array, everything works like a charm
}
render() {
const {items} = this.state;
return (
//some code
<div key={item.id}>
{item.name} {item.price}<img src = {item.url} className="photo"/><button onClick={() => this.addItem(item)}>ADD</button>
</div>
<Basket dataFromParent = {this.getItems} />
</div>
and Basket class
class Basket extends React.Component {
constructor(props) {
super(props);
this.chosenItems = [];
}
state = {
items: []
};
componentDidUpdate()
{
this.chosenItems = this.props.dataFromParent;
console.log(this.props.dataFromParent);
}
render() {
return (
<div>
<h2>{this.chosenItems}</h2>
<h2>{this.props.dataFromParent}</h2>
</div>
);
}
}
export default Basket;
the problem is console log shows "undefined". Could you tell me what I am doing wrong? Or maybe my entire approach is incorrect and I should look for another solution?
Update
class Home extends Component {
state = {
items: [],
chosenItems []
};
// code to import JSON from backend API, irrelevant, that part works fine
addItem(item){
this.setState(prev => ({
chosenItems: [...prev.chosenItems, item]
}))
}
render() {
const {items, chosenItems} = this.state;
return (
<div>
<div><Basket chosenItems ={this.state.chosenItems} /></div>
<Router>
<div className="container">
<ul>
<Link to="/login">login</Link>
<Link to="/basket">basket</Link>
</ul>
<Route path="/login" component={Login} />
<Route path="/basket" component={Basket} />
</div>
</Router>
<div>
{items.map(item =>
<div key={item.id}>
{item.name} {item.price} {item.quantity} <img src = {item.url} className="photo"/><button onClick={() => this.addItem(item)}>Add!</button>
</div>
)}
</div>
</div>
);
}
}
class Basket extends React.Component {
render() {
return (
<div>
{this.props.chosenItems.map(item =>
<div key={item.id}>
{item.name}{item.price}
</div>
)}
</div>
);
}
}
and that works, but the chosenItems array is printed immediatelty where
<Basket chosenItems ={this.state.chosenItems} />
is located after the button is pressed. And when I click on basket redirection I get
TypeError: Cannot read property 'map' of undefined
Firstly, you must understand that things that you don't set in state don't cause a re-render and hence an updated data isn't reflected on to the UI or passed onto the children.
Secondly, you do not need to store the data passed from parent in child again, you can directly use it from props
class Home extends Component {
state = {
items: [],
chosenItems []
};
// code to import JSON from backend API, irrelevant, that part works fine
addItem(item){
this.setState(prev => ({
chosenItems: [...prev.chosenItems, item]
}))
}
render() {
const {items} = this.state;
return (
<div>
//some code
<div key={item.id}>
{item.name} {item.price}<img src = {item.url} className="photo"/><button onClick={() => this.addItem(item)}>ADD</button>
</div>
<Basket chosenItems ={this.state.chosenItems} />
/div>
)
}
}
class Basket extends React.Component {
render() {
return (
<div>
<h2>{this.props.chosenItems.map(item=> <div>{item.name}</div>)}</h2>
</div>
);
}
}
export default Basket;
I see a couple of problems in the snippet -
You are passing this.getItems to your child component as props. It's never defined in the parent. I think it should have been items array state that you have created.
chosenItems should have been a state and you should dig deeper on how to update a state. There is a setState function, learn abt it.
In child, again the the constructor is written like parent's with chosenItems and items which is not needed. You can use them from props.
Please have a look on https://reactjs.org/docs/react-component.html#setstate how to mutate the state of a component. You will find few more basics over this document.
The reason you are getting undefined in the log is because the this.getItems() in Home component is returning undefined either there is no such method or probably the state variable itself is undefined.
In a nutshell few things:
When you want to pass an array to child component, it is as simple as passing any object or property For eg. (I am hoping you want to pass the choosen items to Basket component)
Always initialise state in constructor.
so chooseItems and items should be a part of state and inside constructor.
Your code should look like:
class Home extends Component {
constructor(props) {
super(props)
this.state = {
chosenItems: [],
items: [],
}
}
addItem(item){
this.setState({
chosenItems: [...this.state.chosenItems, item]
})
console.log(this.chosenItems); //logs created array, everything works like a charm
}
render() {
const {items, chooseItems} = this.state;
return (
items.map(item => {
return (
<div key={item.id}>
{item.name} {item.price}<img src = {item.url} className="photo"/>
<button onClick={() => this.addItem(item)}>ADD</button>
</div>
)
})
<Basket dataFromParent={chooseItems} />
div>
)
}
}
and the Basket component would not need constructor since the required data is coming from parent component:
class Basket extends React.Component {
render() {
return (
<div>
{this.props.dataFromParent.map(item => <h2>{this.props.dataFromParent}</h2>)}
</div>
);
}
}
export default Basket;

Map creates only one li - React

I'm making a component that creates Tabs when you click on an item in the navigation menu.
All the controls I do in the father to be able to pass the states between the brothers without problems.
As it is now my code when clicking on a menu item creates a unique <li> and shows the name. If you click on the entire menu item, it does not create a new <li>, but rather updates the old one with the new menu information.
I need that every time I press a menu item a new <li> with its content is created.
I edit my code with #technogeek1995 changes and this the final solution:
class App extends Component {
constructor(props, context){
super(props, context);
["openTabs",].forEach((method) => {
this[method] = this[method].bind(this);
});
this.state = {
navigation: {
menu: [],
},
tabs:{
tabsLi:[],
},
textvalue : "",
showtabs: true,
}
}
componentDidMount() {
fetch('json_menu.php')
.then(response => response.json())
.then(data =>{
this.setState({navigation: data});
//console.log(data)
})
}
openTabs(e, url, iframe, trdtitle){
e.preventDefault();
const state = {...this.state};
state.textvalue = trdtitle.split();
state.tabs.tabsLi.push(state.textvalue);
console.log(state.tabs.tabsLi)
this.setState({ state });
this.setState({
showtabs: false,
});
}
class Tabs extends Component {
render(){
const renderTabs = tabs =>{
return(
<div id="content-tabs" className="tabs">
{( this.props.showtabs)
? (
<>
<div className="waiting-leads">
<p>Parece que todavía no hay ningún lead...</p>
<h3>¡Ánimo, ya llega!</h3>
<img src={imgDinosaurio} alt="Dinosaurio"></img>
</div>
</>
) : (
<ul id="resizable" className="content" >
{this.props.tabs.tabsLi.map((value, index) => {
return (
<li key={index}>
<span>{value}</span>
<Icon icon="cerrar" className='ico-cerrar' onClick={remove_tab(index)}/>
</li>
)
})}
</ul>
)}
</div>
);
}
return (
<>
{renderTabs(this.props.tabs.tabsLi)}
</>
)
}
}
This is the code that is generated when you click on the menu item. The <span> is the one that is updated and no new <li> is created. Ventas is my element menu name.
<ul id="resizable" class="content">
<li>
<span>Ventas</span>
<svg class="ico-cerrar">path</svg>
</li>
</ul>
The issue appears to be related to mutating the state directly. You should see some warnings in the console/terminal about mutating react's state directly. I have updatd your openTabs function so that it no longer mutates the state directly. Instead, I copy state to a local variable, perform the mutations of the local state object. Then, I call setState with the locally updated state object. React will automatically pick up the changes to the state and render the page with the (newly) updated state.
openTabs(e, url, iframe, trdtitle){
e.preventDefault();
const state = {...this.state};
state.textvalue = trdtitle.split();
state.navigation.menu.push(state.textvalue);
state.showtabs = false;
this.setState({ state });
}
Tabs Component needed to be updated to iterate over the list, rather than over the string so it will create a <li> for every element in state.navigation.menu. remove_tab needed to be wrapped in {} instead of "" as well.
class Tabs extends Component {
render() {
return ( <
div id = "content-tabs"
className = "tabs" > {
(this.props.showTabs) ? (
<div className = "waiting-leads" >
<p> Parece que todavía no hay ningún lead... </p>
<h3> ¡Ánimo, ya llega! </h3>
<img src={imgDinosaurio} alt="Dinosaurio"/>
</div>
) : (
<ul id = "resizable" className = "content" >
{this.props.tabs.map((value, index) => (
<li key={index} >
<span>{value}</span>
<Icon icon = "cerrar" className = 'ico-cerrar' onClick={remove_tab(index)} / >
</li>
)
} </ul>
)} </div>
);
}
}
You should use the callback version of state and use the spread syntax to create new objects with new references so that React detects the change in state.
this.state.navigation.menu.push(this.state.textvalue)
Also, this line will push the old textValue and not the new one which is trdtitle.split()
openTabs(e, url, iframe, trdtitle){
e.preventDefault();
const textValue = trdtitle.split()
this.setState(state => ({
textvalue,
showtabs: false,
navigation: {
...state.navigation,
menu: [ ...state.navigation.menu, textValue ]
}
}));
}

Cannot change the state of parent component and re-render

im new to React, trying to make some simple 'Chat' app, stuck a bit in some feature.
im trying to make user list, that onClick (on one of the user) it will change the class (to active), and when hitting another user it will set the active class to the new user.
tried a lot of things, managed to make it active, but when hitting another user, the old one & the one receive the 'active' class.
here is my Parent componenet
class Conversations extends React.Component {
constructor(props) {
super(props);
this.loadConversations = this.loadConversations.bind(this);
this.selectChat = this.selectChat.bind(this);
this.state = { count: 0, selected: false, users: [] }
}
selectChat = (token) => {
this.setState({ selected: token });
}
loadConversations = (e) => {
$.get('/inbox/get_conversations', (data) => {
let r = j_response(data);
if (r) {
this.setState({ count: r['count'], users: r['data']});
}
});
}
componentDidMount = () => {
this.loadConversations();
}
render() {
return (
<div>
{this.state.users.map((user) => {
return(<User selectChat={this.selectChat} selected={this.state.selected} key={user.id} {...user} />)
})}
</div>
)
}
here is my Child componenet
class User extends React.Component {
constructor(props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
this.state = {
token: this.props.token,
selected: this.props.selected,
username: this.props.username
}
}
handleSelect = (e) => {
//this.setState({selected: e.target.dataset.token});
this.props.selectChat(e.target.dataset.token);
}
render() {
return (
<div data-selected={this.props.selected} className={'item p-2 d-flex open-chat ' + (this.props.selected == this.props.token ? 'active' : '')} data-token={this.props.token} onClick={(e) => this.handleSelect(e)}>
<div className="status">
<div className="online" data-toggle="tooltip" data-placement="right" title="Online"></div>
</div>
<div className="username ml-3">
{this.props.username}
</div>
<div className="menu ml-auto">
<i className="mdi mdi-dots-horizontal"></i>
</div>
</div>
)
}
Any help will be great...hope you can explain me why my method didnt work properly.
Thank you.
You can make use of index from map function to make element active.
Initially set selected to 0;
this.state = { count: 0, selected: 0, users: [] }
Then pass index to child component,also make sure you render your User component when you are ready with data by adding a condition.
{this.state.users.length > 0 && this.state.users.map((user,index) => {
return(<User selectChat={this.selectChat} selected={this.state.selected} key={user.id} {...user} index={index} />)
})}
In child component,
<div data-selected={this.props.selected} className={`item p-2 d-flex open-chat ${(this.props.selected === this.props.index ? 'active' : '')}`} data-token={this.props.token} onClick={() => this.handleSelect(this.props.index)}>
...
</div>
handleSelect = (ind) =>{
this.props.selectChat(ind);
}
Simplified Demo using List.

Updating the state of many children at once

I am trying to control the visibility of GalleryItem components by toggling skill components on/off that were used to create those projects. I would like to:
Show ALL if no skills have been toggled
If one or more skills are toggled, show only GalleryItems that use that skill
The selected skills are stored in state.portfolioTypes of Portfolio component - this works as expected.
I simply do not know how to call the updateDisplay method on each of my GalleryItem components from the click handler of my Skill components.
If someone can help me do that I am flying! Thanks.
I have tried pushing my GalleryItem components into an array that is in the parent state so that I can iterate over the array when toggling a Skill, but despite seeing the component objects when I log the array to the console, they are not rendered in the UI - instead are rendered numbers 13-24 (not sure why...)
resumeData.skills looks like:
skills: ["Branding", "Design", "UX", "Marketing", "Print", "Javascript", "HTML", "Grunt", "JQuery", "LessCSS", "Sketch", "Photoshop", "Illustrator", "Bootstrap"]
The item passed to GalleryItem class looks like:
{
imagePath: "images/portfolio/monster.png",
name: "Monster.com",
description: "Web design, UI Development and Art Direction",
modalImagePath: "images/portfolio/lrg/monster.png",
modalName: "Web design, UI Development and Art Direction",
modalDescription: "Working for one of the internet's biggest brands, I developed UI for internal incubator projects, components of the global web application and helped with the full UI redesign of the job seeker experience.",
modalCategories: ["Branding", "Design", "UX", "Photoshop", "Illustrator"],
url: "http://www.monster.com"
}
My Portfolio class containing Skill class and GalleryItem classes:
(I have removed some code not relevant to this question)
import React, { Component } from 'react';
export default class Portfolio extends Component {
constructor(props){
super(props);
this.state = {
portfolioTypes: [],
galleryItems: []
}
this.togglePortfolioItems = this.togglePortfolioItems.bind(this);
}
togglePortfolioItems(item){
//render only portfolio items with selected tags
console.log("togglePortfolioItems", item);
let portfolioTypes = this.state.portfolioTypes;
if(!item.isToggleOn){
portfolioTypes.push(item.type);
}else{
portfolioTypes.splice(portfolioTypes.indexOf(item.type), 1);
}
this.setState({portfolioTypes: portfolioTypes});
console.log(this.state.portfolioTypes, portfolioTypes);
}
render() {
let resumeData = this.props.resumeData;
let togglePortfolioItems = this.togglePortfolioItems;
let portfolioTypes = this.state.portfolioTypes;
let galleryItems = this.state.galleryItems;
return (
<React.Fragment>
<section id="portfolio">
<div className="row">
<div className="twelve columns collapsed">
<h1>Check Out Some of My Works.</h1>
<div className="skillToggles">
{resumeData.skills.map((item,index) => (
<Skill
skillName={item}
togglePortfolioItems={togglePortfolioItems}
galleryItems={galleryItems}
/>
))}
</div>
{/* portfolio-wrapper */}
<div id="portfolio-wrapper" className="bgrid-quarters s-bgrid-thirds cf">
{resumeData.portfolio.map((item,index) => (
galleryItems.push(<GalleryItem
item={item}
index={index}
portfolioTypes={portfolioTypes}
/>)
))}
</div> {/* portfolio-wrapper end */}
</div> {/* twelve columns end */}
</div> {/* row End */}
</section> {/* Portfolio Section End*/}
</React.Fragment>
);
this.setState({galleryItems: galleryItems});
}
}
class Skill extends Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: false,
type: props.skillName.toLowerCase()
};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
this.props.togglePortfolioItems(this.state);
let galleryItems = this.props.galleryItems;
//loop through all galleryItems and set the display of each
galleryItems.map(galleryItem =>(
console.log(galleryItem);
//I would like to fire updateDisplay on galleryItem here
));
}
render() {
let skillName = this.props.skillName;
let skillNameId = skillName.toLowerCase();
return (
<React.Fragment>
<a href="" className={"skill "+(this.state.isToggleOn ? 'on' : 'off')} onClick={this.handleClick}>
{skillName}
</a> {/* Skill Section End*/}
</React.Fragment>
);
}
}
class GalleryItem extends Component{
constructor(props) {
super(props);
let portfolioTypes = this.props.portfolioTypes;
var displayed = true;
this.state = {
displayed: displayed
};
}
updateDisplay(){
let portfolioTypes = this.state.portfolioTypes;
let displayed = false;
if(portfolioTypes.length === 0){
displayed = true;
}else{
for(var x=0; x<portfolioTypes.length; x++){
let cat = portfolioTypes[x];
if(portfolioTypes.indexOf(cat) > -1){
displayed = true;
}
};
}
this.setState({displayed: displayed});
}
render() {
let item = this.props.item;
let index = this.props.index;
return (
<React.Fragment>
<div className={"columns portfolio-item "+(this.state.displayed ? "" : "hide ")+item.modalCategories.sort().join(" ").toLowerCase()}>
<div className="item-wrap">
<a href={"#modal-0"+index} title={item.name}>
<img alt src={item.imagePath} />
<div className="overlay">
<div className="portfolio-item-meta">
<h5>{item.name}</h5>
<p>{item.description}</p>
</div>
</div>
<div className="link-icon"><i className="icon-plus" /></div>
</a>
</div>
</div>
</React.Fragment>
);
}
}
When I toggle a skill, I would like the gallery to update to only display GalleryItems that used the selected skills.
Perhaps you can also suggest improvements to my approach, as there is probably a better/easier/more robust way to achieve this.
change updateDisplay function like this
updateDisplay(){
let portfolioTypes = this.props.portfolioTypes;
let displayed = false;
if(portfolioTypes.length === 0){
displayed = true;
}else{
for(var x=0; x<portfolioTypes.length; x++){
let cat = portfolioTypes[x];
if(portfolioTypes.indexOf(cat) > -1){
displayed = true;
}
};
}
return displayed;
}
Then Define a variable inside render
var displayed = this.updateDisplay()
use this variable instead of this.state.displayed
import React, { Component } from 'react';
export default class Portfolio extends Component {
constructor(props){
super(props);
this.state = {
portfolioTypes: []
}
this.togglePortfolioItems = this.togglePortfolioItems.bind(this);
}
togglePortfolioItems(item){
//render only portfolio items with selected tags
console.log("togglePortfolioItems", item);
let portfolioTypes = this.state.portfolioTypes;
if(!item.isToggleOn){
portfolioTypes.push(item.type);
}else{
portfolioTypes.splice(portfolioTypes.indexOf(item.type), 1);
}
this.setState({portfolioTypes: portfolioTypes});
console.log(this.state.portfolioTypes, portfolioTypes);
}
render() {
let resumeData = this.props.resumeData;
let togglePortfolioItems = this.togglePortfolioItems;
let portfolioTypes = this.state.portfolioTypes;
return (
<React.Fragment>
<section id="portfolio">
<div className="row">
<div className="twelve columns collapsed">
<h1>Check Out Some of My Works.</h1>
<div className="skillToggles">
{resumeData.skills.map((item,index) => (
<Skill
skillName={item}
key={index}
togglePortfolioItems={togglePortfolioItems}
/>
))}
</div>
{/* portfolio-wrapper */}
<div id="portfolio-wrapper" className="bgrid-quarters s-bgrid-thirds cf">
{resumeData.portfolio.map((item,index) => (
<GalleryItem
item={item}
index={index}
key={index}
portfolioTypes={portfolioTypes}
/>
))}
</div> {/* portfolio-wrapper end */}
</div> {/* twelve columns end */}
</div> {/* row End */}
</section> {/* Portfolio Section End*/}
</React.Fragment>
);
}
}
class Skill extends Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: false,
type: props.skillName
};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
this.props.togglePortfolioItems(this.state);
}
render() {
let skillName = this.props.skillName;
return (
<React.Fragment>
<a href="#" className={"skill "+(this.state.isToggleOn ? 'on' : 'off')} onClick={this.handleClick}>
{skillName}
</a> {/* Skill Section End*/}
</React.Fragment>
);
}
}
class GalleryItem extends Component{
constructor(props) {
super(props);
let portfolioTypes = this.props.portfolioTypes;
}
updateDisplay(){
console.log("updateDisplay");
let portfolioTypes = this.props.portfolioTypes;
let item = this.props.item;
let displayed = false;
if(portfolioTypes.length === 0){
displayed = true;
}else{
for(var x=0; x<portfolioTypes.length; x++){
let cat = portfolioTypes[x];
if(item.modalCategories.indexOf(cat) > -1){
displayed = true;
}
};
}
return displayed;
}
render() {
let item = this.props.item;
let index = this.props.index;
var displayed = this.updateDisplay();
return (
<React.Fragment>
<div className={"columns portfolio-item "+(displayed ? "" : "hide ")+item.modalCategories.sort().join(" ")}>
<div className="item-wrap">
<a href={"#modal-0"+index} title={item.name}>
<img alt="Gallery Image" src={item.imagePath} />
<div className="overlay">
<div className="portfolio-item-meta">
<h5>{item.name}</h5>
<p>{item.description}</p>
</div>
</div>
<div className="link-icon"><i className="icon-plus" /></div>
</a>
</div>
</div>
</React.Fragment>
);
}
}

Passing state back to child component

I'm trying to figure out how can i properly pass state back to the child component.
Currently I have list of items and everytime i click on one of the items it changes state of "selectedVideo" variable in parent component. And then I would like to add class to the item that corresponds to that state in that child component. Basically when I click on that item in that list it become highlighted because it just changed the state of parent component.
So the main parent component is here:
index.js
class App extends Component {
constructor(props) {
super(props)
this.state = {
videos2:[],
selectedVideo:null
}
this.DMSearch()
}
DMSearch(term){
fetch(`https://api.dailymotion.com/videos?fields=description,id,thumbnail_60_url,title,url,&limit=5&search=${term}`)
.then(result => result.json())
.then(videos2 => {
//console.log(videos2.list[0]);
this.setState({
videos2: videos2.list,
selectedVideo: videos2.list[0]
});
//console.log(this.state.selectedVideo);
});
}
render () {
const DMSearch = _.debounce((term) => { this.DMSearch(term)}, 400);
return (
<div>
<SearchBar onSearchTermChange= {DMSearch}/>
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo=>this.setState({selectedVideo})}
videos2={this.state.videos2}/>
</div>
)
}
}
Now the child component which changes state onclick
video_list_item.js
const VideoListItem = ({video, onVideoSelect}) => {
const imageUrl = video.thumbnail_60_url;
return (
<li onClick={() => onVideoSelect(video)} className="list-group-item">
<div className="video-list media">
<div className="media-left">
<img className="media-obj" src={imageUrl}/>
</div>
<div className="media-body">
<div className="media-heading">{video.title}</div>
</div>
</div>
</li>
);
};
And what I want is to add class "active" to this specific line
<li onClick={() => onVideoSelect(video)} className="list-group-item">
Based on the state of selectedVideo that changed in index.js after clicking on that component.
Also here is the code for the whole list.
video_list.js
const VideoList = (props) => {
const videoItems = props.videos2.map((video)=>{
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.id}
video={video} />
)
})
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
)
}
You have to pass the selectedVideo state of your App to the VideoList component,
<VideoList
videos2={this.state.videos2}
onVideoSelect={selectedVideo=>this.setState({selectedVideo})}
selectedVideo={this.state.selectedVideo}
/>
which in turn passes it to each VideoListItem
const videoItems = props.videos2.map((video)=>{
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.id}
video={video}
active={video === props.selectedVideo}
/>
)
})
so each item can compare itself to the selectedVideo and display an 'active' class if needed.

Resources