I am new to React and have been trying to figure out how to control an array of components from a parent component. My job is to create a site where I can add or subtract names to a list, but have gotten stuck on the best way to do it. In this case, I created an array of react components, each with controlled input for title boxes and each with a delete button that would call the parent function's remove function through the prop system. However, I noticed that when doing so, the array in the parent function would remain correct, while the id's of the children components would not change to be reordered, thereby ruining subsequent removals. I am sure I am doing this wrong and would like to find a better and more efficient way of doing this. Thanks!
import React, {Component} from 'react';
import Music from './music'
import axios from 'axios';
var childrenComponents = [];
class Selection {
constructor(){
this.music = '';
this.beginning = 0;
this.the_end = 0;
}
setTitle=(title)=>{
this.music = title;
}
setStart=(start)=>{
this.beginning = start;
}
setEnd=(end)=>{
this.the_end = end;
}
}
class Practice extends React.Component{
constructor(props){
super(props);
this.state = {
number_music: 0,
number: 0,
selections: Array(0).fill(null),
deletions: 0,
}
this.addAnotherSong = this.addAnotherSong.bind(this);
this.removeSong = this.removeSong.bind(this);
this.renderMusicPlayed = this.renderMusicPlayed.bind(this);
}
removeSong(index){
if((this.state.number_music-1) >= 0){
alert(index);
for(var i = 0; i < (this.state.selections.length-1); i++){
console.log(this.state.selections[i].music);
}
childrenComponents.splice(index, 1);
this.setState({selections: this.state.selections.filter((_, i) => i !== index),
number_music: this.state.number_music - 1,
deletions: this.state.deletions += 1});
console.log("========================");
for(var i = 0; i < (this.state.selections.length-1); i++){
console.log(this.state.selections[i].music);
}
console.log("///////////////////////////////////////////////////");
}
}
addAnotherSong(){
this.state.selections.push(new Selection());
var i = this.state.number_music;
childrenComponents.push(
<Music key={i} number={i} subtract={this.removeSong}
Title={this.state.selections[i].music} Start={this.state.selections[i].beginning}
End={this.state.selections[i].the_end} changeTitle={this.state.selections[i].setTitle}
changeStart={this.state.selections[i].changeStart} changeEnd={this.state.selections[i].changeEnd}/>
);
this.setState({ number_music: this.state.number_music += 1, number: this.state.number += 1});
}
renderMusicPlayed(){
return (
<div>
{childrenComponents}
</div>
);
}
render(){
return(
<div>
<button onClick={()=> this.props.practice()}>Log Practice Session</button>
<h1>{this.props.time}</h1>
<form >
Description: <input type="form" placeholder="How did it go?" name="fname"/><br/>
</form>
{this.renderMusicPlayed()}
<button onClick={()=>this.addAnotherSong()}>Add Another Piece</button>
{this.state.number_music}
</div>
);
}
}
export default Practice;
That is the parent.
This is the Child:
import React, {Component} from 'react';
import InputBox from './input';
class Music extends React.Component{
constructor(props){
super(props);
this.state = {
title: null,
start: null,
end: null
}
}
componentWillReceiveProps(props){
this.setState({ title: this.props.Title});
}
render(){
return(
<div>
<InputBox initialValue={this.props.number} cValue={this.props.Title} identity={this.props.number} updateInput={this.props.changeTitle} />
<InputBox initialValue="Starting Measure" cValue={this.props.Start} identity={this.props.number} updateInput={this.props.changeStart} />
<InputBox initialValue="Ending Measure" cValue={this.props.End} identity={this.props.number} updateInput={this.props.changeEnd} />
<button onClick={()=> this.props.subtract(this.props.number)}>Delete</button>
</div>
)
}
}
export default Music;
And this is the grand child so to speak:
import React,{Component} from 'react';
class InputBox extends React.Component{
constructor(props){
super(props);
this.state = { value: this.props.initialValue, text: "" }
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
this.setState({value: event.target.value});
this.props.updateInput(this.state.value, this.props.identity);
}
render(){
return(
<input type="text" onChange={this.handleChange} value={this.state.cValue}></input>
)
}
}
export default InputBox;
I guess my main question is what is the ideal way for handling this kind of problem.
The reason your IDs are not changing is because you're pushing fully formed components to the array.
Imagine we have 3 components - formatting will be a little weird, but hopefully it illustrates the point:
[ Music: { id: 0 }, Music: { id: 1 }, Music: { id: 2 } ]
When we click the delete button, say on Music with id: 1, we end up with this:
[ Music: { id: 0 }, Music: { id: 2 } ]
We spliced the right Music out, but we now have a wrong index - we never actually changed the Music with id: 2. It would be much easier (in my opinion) to just dynamically construct your Music components in the render function.
Realistically, your childrenComponents array isn't all that useful - the Music components created in it are all created with the index i in mind:
Title={this.state.selections[i].music}
Start={this.state.selections[i].beginning}
End={this.state.selections[i].the_end}
and so on and so forth.
We could simplify this pretty easily, and consolidate all of this into one array.
Imagine we had an array field state.children, which looked something like this:
[
{ title: _____, start: _____, end: ____, ... },
{ title: _____, start: _____, end: ____, ... },
{ title: _____, start: _____, end: ____, ... },
]
This is a lot more clear in a huge way: our data is consolidated in one, singular place, and we aren't tying them together by some arbitrary index. You've done this in a sense with your selections array, but because you are also using childrenComponents, you're double managing what is essentially the same data.
We can pretty easily render it, too, with something along the lines of:
render() {
{
this.state.children.map((child, index) => (
<Music key={index}
number={index}
subtract={this.removeSong}
Title={this.state.children[index].title}
...
/>
);
}
}
That helps us decouple the actual meat of our objects (title, beginning, ending, etc) from their position in the array, which don't really mean anything and are just getting in the way here. That lets us splice up our array however we see fit, and be certain that we're not breaking any relationships between our components and their indexes.
Related
My JSX won't show up properly on my React webpage instead I get this output:
<div class='card'>NaNSasha<img src= NaN />Boddy Cane</div>.
The component:
import React, {Component} from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
class App extends Component{
state = {
string : '',
}
componentDidMount(){
let posts = [
{
title: 'somebody toucha my spaghet',
author: 'Karen',
image:'https://media-cdn.tripadvisor.com/media/photo-s/11/69/c7/f9/spagetti.jpg',
location: 'Jimmy John',
description: 'This spagetti is amazing'
},
{
title: `I love food`,
author: 'Sasha',
image:'https://hoodline.imgix.net/uploads/story/image/610603/donuts2.jpg?auto=format',
location: 'Boddy Cane',
description: 'cjndwsijnjcinjw'
}
];
for(let i =0; i < posts.length; i ++){
const header = `<div class='card'>${+posts[i].title}`;
const body = posts[i].author;
const image = `<img src= ${+posts[i].image} />`;
const description = `${posts[i].location}</div>`;
const concatThis = header + body + image + description
this.setState({
string: concatThis
});
};
};
render(){
return(
<div className='container'>
{this.state.string}
</div>
)
}
}
export default App;
P.S I'm a student
This is what you're looking for. The expression +{} is evaluated as NaN. But please use list rendering.
const image = `<img src= ${+posts[i].image} />`;
^ here
It seems that you are trying to build a string which you then store in a state and render that string after it has been updated. This is unfortunately not how you should use React.
The state should only be raw data, like the posts array with objects. It holds the content and data of the component and should not concern itself on other tasks than that. You obviously can put any type of data in a state, like a string.
state = {
title: 'My food blog',
description: 'Awesome stuff about food',
posts: [
{
title: 'somebody toucha my spaghet',
author: 'Karen',
image:'https://media-cdn.tripadvisor.com/media/photo-s/11/69/c7/f9/spagetti.jpg',
location: 'Jimmy John',
description: 'This spagetti is amazing'
},
{
title: `I love food`,
author: 'Sasha',
image:'https://hoodline.imgix.net/uploads/story/image/610603/donuts2.jpg?auto=format',
location: 'Boddy Cane',
description: 'cjndwsijnjcinjw'
}
]
}
The componentDidMount method is triggered whenever the component has been placed on the page and is now working. In there you can do things like making a change to the data or downloading data from the server. It would make sense that you would do that there because then you would first show your component, maybe show it that it is loading and then fetch data from the server. After that is done, update the state of the component with the new data and the render method will be called with the new data. For example (for illustration purposes):
componentDidMount() {
fetch('urlofdatathatyouwant') // Uses AJAX to get data from anywhere you want with the Fetch API.
.then(response => response.json()) // Tells it to read turn the response from JSON into an usable JavaScript values.
.then(data => {
this.setState({
posts: data // Use the new data to replace the posts. This will trigger a new render.
});
});
}
The render method should primarely concern itself with the presentation of the data in your state. In this case it should loop over the elements in the posts state and create a React element for each post.
render() {
const { posts } = this.state;
return(
<div className='container'>
{posts.map(({ title, author, image, location, description }) => (
// Loop over each post and return a card element with the data inserted.
<div className="card">
<span>{title}</span>
<span>{author}</span>
<img src={image} alt={title}/>
<span>{location}</span>
<span>{description}</span>
</div>
))}
</div>
)
}
All put together it would look like this the example below. So the state only holds the data, componentDidMount is a place to do manipulation of your data after the component is on the page and render only outputs the HTML that you need to create with JSX.
import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
class App extends Component{
state = {
posts: [
{
title: 'somebody toucha my spaghet',
author: 'Karen',
image:'https://media-cdn.tripadvisor.com/media/photo-s/11/69/c7/f9/spagetti.jpg',
location: 'Jimmy John',
description: 'This spagetti is amazing'
},
{
title: `I love food`,
author: 'Sasha',
image:'https://hoodline.imgix.net/uploads/story/image/610603/donuts2.jpg?auto=format',
location: 'Boddy Cane',
description: 'cjndwsijnjcinjw'
}
]
}
componentDidMount() {
// Do something here with the posts if you need to.
}
render() {
const { posts } = this.state;
return(
<div className='container'>
{posts.map(({ title, author, image, location, description }, index) => (
<div key={index} className="card">
<span>{title}</span>
<span>{author}</span>
<img src={image} alt={title}/>
<span>{location}</span>
<span>{description}</span>
</div>
))}
</div>
)
}
}
export default App;
You could even make it a bit nicer by making the card element a component as well. And since it does not have any functionality (yet) it only has to control the output.
const Card = ({ title, author, image, location }) => (
<div className="card">
<span>{title}</span>
<span>{author}</span>
<img src={image} alt={title}/>
<span>{location}</span>
<span>{description}</span>
</div>
)
And then import the card into your App component and use it in the render method.
// App.jsx render.
render() {
const { posts } = this.state;
return(
<div className='container'>
{ /* ...post taking all the properties of each post and passes them to the card element */ }
{posts.map((post, index) => <Card key={index} {...post} />)}
</div>
)
}
I am trying to have a set of input fields right after one another like a terminal.
The Page component looks like this
import React, { Component } from "react";
import NewLine from "./newLine";
export class Page extends Component {
state = {
numberOfLine: 0,
lines: [{ value: "", id: 0, displayInputs: true}]
};
render() {
return (
<div className="container">
<div className="terminal">
<p className="prompt">
Hey there! This is a pre rendered line.
</p>
{this.state.lines.map(l => (
<NewLine
key={this.state.numberOfLine}
handelWhatever={this.handelWhatever}
line={l}
></NewLine>
))}
</div>
</div>
);
}
handelWhatever = (string_value, Tid) => {
// console.log(string_value, Tid);
// console.log(this.state.lines.filter(l => l.id != Tid));
const num = this.state.numberOfLine + 1;
this.setState({
numberOfLine: this.state.lines.length + 1,
lines: [
...this.state.lines.filter(line => line.id !== Tid),
{ value: string_value, id: Tid, displayInput: false },
{ value: "", id: num, displayInput: true }
]
});
};
export default Page;
and my NewLine component looks like this
import React, { Component } from "react";
export class NewLine extends Component {
state = {
id: this.props.line.id,
value: this.props.line.value,
displayInput: this.props.line.displayInput
};
render() {
return (
<React.Fragment>
<p className=output>
{this.state.displayInput && (
<input
type="text"
className="here"
value={this.state.value}
onChange={this.handleChange}
onKeyDown={this.handelEnter}
/>
)}
{this.state.value}
</p>
</React.Fragment>
);
}
handleChange = event => {
this.setState({ value: event.target.value });
};
handelEnter = event => {
if (event.key === "Enter") {
this.props.handelWhatever(event.target.value, this.state.id);
}
};
}
export default NewLine;
When I enter the "something" in the input it should make a NewLine component and delete the input from the previous one so that user can type on the newly rendered line that is why I have bool in the New Line state.
The states updates perfectly but when i user input it takes all the previous ones and render them, i.e,
Initial Stage
> Hey there! This is a pre rendered line.
>
User Input : 'ls'
> Hey there! This is a pre rendered line.
> ls
>
User Input : 'cd'
> Hey there! This is a pre rendered line.
> ls
> ls
> cd
and so on
I don't know what is going on I tried printing the state of the Parent component and it has desired number of lines In the map if I do console.log just after one input I will get
{value: "ls", id: 1, displayInput: false}
{value: "ls", id: 1, displayInput: false}
{value: "", id: 2, displayInput: true}
console logging in map is like this
{this.state.lines.map(l => {
console.log(l);
return (
<NewLine
key={this.state.numberOfLine}
handelWhatever={this.handelWhatever}
line={l}
></NewLine>
);
})}
You need to use the id of the line for the key for each NewLine component.
Also, you need to use {this.props.line.value} instead of {this.state.value} in the NewLine component.
See below
<React.Fragment>
<p className=output>
{this.state.displayInput && (
<input
type="text"
className="here"
value={this.state.value}
onChange={this.handleChange}
onKeyDown={this.handelEnter}
/>
)}
{this.props.line.value} OR {this.state.value}
</p>
</React.Fragment>
See this codepen.
In the code pen I use a div instead of React.Fragment but that's just because the fragment was throwing an error in a codepen.
EDIT
It actually works with this.state.value so depending on use case, both this.props.line.value and this.state.value works.
Good evening,
as a learning project I want to build a simple "Learning Cards" App. The structure is quite simple: you have cards with questions. After a button click, you can show the correct solution. You can also click on "Question solved" to move the learning card to the absolved cards.
I am struggling to realize the "moving the learning card to the absolved" cards part. I have a "questions" array. After "onSolvedClick" the solved card gets copied to the "solved" array which is set as the new solved state.
When I click on the "Frage gelöst" (question solved) button, a new card appears in the solved questions region. The problem is: the new card is empty (without the question / answer). It would be great if someone could help me at this point! I already spent hours on this problem today.
I guess my mistake is within the App.Js code, probably in "onSolvedKlick" or "solveFragen".
Thanks a lot!
App.Js:
import React, {Component} from 'react';
import CardList from './CardList.js';
import { fragen } from './fragen';
import SearchBox from './SearchBox';
class App extends Component { // As Class to access objects
constructor () {
super();
this.state = { // State needed to change state
fragen: fragen,
solved : [] ,
searchfield: ''
}
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
}
onSolvedKlick = (id) => {
console.log("Klick on solved"+id);
var frage = this.state.fragen.filter(function(e) // Bei Klick auf Solved: filtere aus Ursprungsarray das Element mit gelöster iD
{
return e.id === id;
});
console.log(frage);
const newSolvedArray = this.state.solved.slice();
newSolvedArray.push(frage);
this.setState({solved: newSolvedArray});
}
render(){ // DOM rendering
const filteredFragen = this.state.fragen.filter(fragen =>{
return fragen.frage.toLowerCase().includes(this.state.searchfield.toLowerCase());
})
const solveFragen = this.state.solved;
return(
<div className='tc'>
<h1>Learning Cards</h1>
<SearchBox searchChange={this.onSearchChange}/>
<h2>Cards: To be learned!</h2>
<div>
<CardList fragen={filteredFragen} onSolvedKlick={this.onSolvedKlick}/>
<CardList fragen={solveFragen} onSolvedKlick={this.onSolvedKlick}/>
</div>
</div>
)
}
}
export default App;
CardList.js:
import React from 'react';
import Card from './Card';
const CardList = ({fragen, onSolvedKlick}) => {
const cardComponent = fragen.map( (user, i) => {
return(<Card key={i} id={fragen[i].id} frage = {fragen[i].frage} antwort = { fragen[i].antwort} onSolvedKlick = {onSolvedKlick}/>);
}
)
return (
<div>
{cardComponent}
</div>
);
}
export default CardList;
Card.js:
import React, {Component} from 'react';
import 'tachyons';
class Card extends Component {
constructor(props) {
super(props);
this.state = {
frage : props.frage,
showAnswer : false
};
}
_showAnswer = () => {
const before = this.state.showAnswer;
const after = !before;
this.setState({
showAnswer: after
});
}
render() {
return (
<div className ="fl w-50 w-25-m w-20-l pa2 bg-light-red ma3">
<div>
<h2>{this.props.frage}</h2>
{ this.state.showAnswer && (<div>{this.props.antwort}</div>) }
<p></p>
<input type="button" value="Antwort anzeigen" className ="ma2"
onClick={this._showAnswer.bind(null)}
/>
<input type="button" name="solved" value="Frage gelöst" className = "ma2 bg-light-green"
onClick={() =>this.props.onSolvedKlick(this.props.id)}
/>
</div>
</div>
);
}
}
fragen.js (Questions):
export const fragen = [
{
id: 1,
frage: 'What are trends in CPU design?',
antwort: 'Multi-core processors, SIMD support, Combination of core private and shared caches Heterogeneity, Hardware support for energy control',
topic: 'Cloud'
},
{
id: 2,
frage: 'What is typical for multi-core processors?',
antwort: 'Cache Architecture (L1 private to core, L2 private to tile), Cache Coherence',
topic: 'Cloud'
},
{
id: 3,
frage: 'What memory modes exist?',
antwort: 'Flat mode, Cache Mode, Hybrid Mode',
topic: 'Cloud'
},
{
id: 4,
frage: 'What memory modes exist?',
antwort: 'Flat mode, Cache Mode, Hybrid Mode',
topic: 'Cloud'
},
];
Try this on your onSolvedKlick function:
onSolvedKlick = (id) => {
console.log("Klick on solved"+id);
var frage = this.state.fragen.filter((e) => e.id === id);
this.setState({solved: [...this.state.solved, frage]});
}
Try to avoid so many empty lines.
Also keep your code always in english so it's easier for others to understand. I had the luck to be german too :)
Assuming that you want to move the questions from fragen array to solved array, here is how to do that.
onSolvedKlick = id => {
console.log("Klick on solved" + id);
var elementPos = this.state.fragen.map(function(x) {return x.id; }).indexOf(id); // Find the position of the selected item in the fragen
const currentItem = this.state.fragen.splice(elementPos,1)
const newSolvedArray = this.state.solved;
newSolvedArray.push(currentItem[0]);//splice gives an array
this.setState({ solved: newSolvedArray }, function() {console.log(this.state)});
};
The function is getting the value of a button click as props. Data is mapped through to compare that button value to a key in the Data JSON called 'classes'. I am getting all the data correctly. All my console.logs are returning correct values. But for some reason, I cannot render anything.
I've tried to add two return statements. It is not even rendering the p tag with the word 'TEST'. Am I missing something? I have included a Code Sandbox: https://codesandbox.io/s/react-example-8xxih
When I click on the Math button, for example, I want to show the two teachers who teach Math as two bubbles below the buttons.
All the data is loading. Just having an issue with rendering it.
function ShowBubbles(props){
console.log('VALUE', props.target.value)
return (
<div id='bubbles-container'>
<p>TEST</p>
{Data.map((item,index) =>{
if(props.target.value == (Data[index].classes)){
return (
<Bubble key={index} nodeName={Data[index].name}>{Data[index].name}
</Bubble>
)
}
})}
</div>
)
}
Sandbox Link: https://codesandbox.io/embed/react-example-m1880
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
const circleStyle = {
width: 100,
height: 100,
borderRadius: 50,
fontSize: 30,
color: "blue"
};
const Data = [
{
classes: ["Math"],
name: "Mr.Rockow",
id: "135"
},
{
classes: ["English"],
name: "Mrs.Nicastro",
id: "358"
},
{
classes: ["Chemistry"],
name: "Mr.Bloomberg",
id: "405"
},
{
classes: ["Math"],
name: "Mr.Jennings",
id: "293"
}
];
const Bubble = item => {
let {name} = item.children.singleItem;
return (
<div style={circleStyle} onClick={()=>{console.log(name)}}>
<p>{item.children.singleItem.name}</p>
</div>
);
};
function ShowBubbles(props) {
var final = [];
Data.map((item, index) => {
if (props.target.value == Data[index].classes) {
final.push(Data[index])
}
})
return final;
}
function DisplayBubbles(singleItem) {
return <Bubble>{singleItem}</Bubble>
}
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {
json: [],
classesArray: [],
displayBubble: true
};
this.showNode = this.showNode.bind(this);
}
componentDidMount() {
const newArray = [];
Data.map((item, index) => {
let classPlaceholder = Data[index].classes.toString();
if (newArray.indexOf(classPlaceholder) == -1) {
newArray.push(classPlaceholder);
}
// console.log('newArray', newArray)
});
this.setState({
json: Data,
classesArray: newArray
});
}
showNode(props) {
this.setState({
displayBubble: true
});
if (this.state.displayBubble === true) {
var output = ShowBubbles(props);
this.setState({output})
}
}
render() {
return (
<div>
{/* {this.state.displayBubble ? <ShowBubbles/> : ''} */}
<div id="sidebar-container">
<h1 className="sidebar-title">Classes At School</h1>
<h3>Classes To Search</h3>
{this.state.classesArray.map((item, index) => {
return (
<button
onClick={this.showNode}
className="btn-sidebar"
key={index}
value={this.state.classesArray[index]}
>
{this.state.classesArray[index]}
</button>
);
})}
</div>
{this.state.output && this.state.output.map(item=><DisplayBubbles singleItem={item}/>)}
</div>
);
}
}
ReactDOM.render(<Sidebar />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
The issue here is ShowBubbles is not being rendered into the DOM, instead (according the sandbox), ShowBubbles (a React component) is being directly called in onClick button handlers. While you can technically do this, calling a component from a function will result in JSX, essentially, and you would need to manually insert this into the DOM.
Taking this approach is not very React-y, and there is usually a simpler way to approach this. One such approach would be to call the ShowBubbles directly from another React component, e.g. after your buttons using something like:
<ShowBubbles property1={prop1Value} <etc...> />
There are some other issues with the code (at least from the sandbox) that you will need to work out, but this will at least help get you moving in the right direction.
I am fairly new to React and was wondering if anybody could give me an insight on a problem I am stuck with.
Right now I have a parent(Hello.js) component and two children(Mixer.js and renderCont.js) at the same level.
I am trying to render a list in the Mixer.js and display its corresponding objects in the Hello.js through by passing the values into RenderCont.js. I've gotten to a point where nothing is displayed before I click on any of the list to pass on a object.
From here is where I am stuck: I want the first object of the list to be displayed as a default, at the same time bold the first in the list. And then execute the as I have below.
This is my first time posting a question on stackoverflow so I'm not sure if my question makes sense with the attached codes but I will greatly appreciate any kind of support.
Parent Hello.js:
import React, { Component } from 'react';
import RenderCont from './renderCont.js';
import Mixer from './Mixer';
class Hello extends Component{
constructor(props) {
super(props);
this.state = {
items: [{
id: 0,
name: "First",
background: "white"
}, {
id: 1,
name: "Second",
background: "yellow"
}, {
id: 2,
name: "Third",
background: "blue"
}],
selectedItem: 0
}
this.handle = this.handle.bind(this)
}
handle(value) {
// console.log(this.state.selectedItem);
this.setState({
selectedItem: value
})
}
render() {
const list = this.state.items.map((item) => {
return(item);
})
return (
<div>
<Mixer item={list} onClick={this.handle} selected={this.state.selectedItem}/>
<ul id = "todo" >
<RenderCont item={this.state.selectedItem}/>
</ul>
</div>
)
}
}
export default Hello;
Mixer.js Child1:
import React, { Component } from 'react';
class Mixer extends Component{
constructor(props) {
super(props);
this.state = {
}
this.handleClick = this.handleClick.bind(this);
}
handleClick(item){
this.props.onClick(item);
}
renderTodos(propItems) {
return (
<div>
{propItems.map((item) => (
<li className={this.props.selected === item ? 'media clicked' : 'media'}
key={item.id} onClick = {() => this.handleClick(item)}>
{item.name}
</li>
))}
</div>
)
}
render() {
return (
<div className="yoyoyo">
{this.renderTodos(this.props.item)}
</div>
)
}
}
export default Mixer;
Second Child Comp renderCont.js :
import React, { Component } from 'react';
class RenderCont extends Component{
constructor(props) {
super(props);
}
renderBox(item){
return(
<div style={{color:item.background}}>
{item.id}
{item.name}
</div>
)
}
render() {
return (
<div className="yoyo">
{this.renderBox(this.props.item)}
</div>
)
}
}
export default RenderCont;
and the CSS:
.yoyo{
left: 500px;
background-color:red;
width:500px;
height:500px;
}
.media{
color: black;
}
.clicked{
font-weight: 900;
}
.yoyoyo{
background-color:lightblue;
width:200px;
height:200px;
}
I think the problem is some mismatch between the initial and eventual value of this.props.selected in Mixer.js. You initially set this.state.selectedItem = 0, and this is what is initially passed as the selected prop to Mixer. But the test you apply in that component is
this.props.selected === item ?
While there is one item.id that === 0, there is never an item that === 0. So no items are highlighted at first. But then, once an item is clicked and selectedItem is actually set to an item, the entry is made bold.
So it looks like you need to either make your initial selection equal to the item.id === 0 reference, or consistently refer to items within your components by their id's.