I'm new in ReactJs and I got stuck in this issue and couldn't find any solution by browsing. Please let me know if this has been answered somewhere else.
My first problem is how can I update the component on the same page in React. I thought it should happen automatically when the state is changed but in my case, it doesn't.
What I'm trying to do is API call after onClick on div and shows the Child list instead of the current list in a specific part of the page (component) and this process continues up to end of the parent/child list. but when I click the div, a value of Localstorage is updated, the API successfully called and everything goes to the right place except showing the result on the page. Once I refresh the page the updated list shows well.
I thought maybe it's because I handle onclick in Childlist component but when I move it to the main component nothing changed.
Also, I would be grateful if you have any tips for coding better.
this is my code :
Parent component :
export class MainP5 extends React.Component {
constructor(props) {
super(props);
this.handleNextBtn = this.handleNextBtn.bind(this);
this.handleBackBtn = this.handleBackBtn.bind(this);
this.childItemHandler = this.childItemHandler.bind(this);
this.state = {
Items: '',
doRedirect: false,
doRedirectBack: false,
doNothing: false
};
}
componentDidMount() {
let selectedItemID = localStorage.getItem('SelectedTemplateID');
let itemIdList = "[\"" + localStorage.getItem('SelectedSectionsChildID') + "\"]";
request
.post(conn.GetItems)
.query({templateId: selectedItemID})
.send(itemIdList)
.set('Authorization', `Bearer ${localStorage.getItem("tokenId")}`)
.set('Content-Type', 'application/json-patch+json')
.set('accept', 'application/json')
.set('dataType', 'jsonp')
.end((err, res) => {
this.setState({
Items: res != null ? res.text : ""
});
});
}
childItemHandler(selectedID) {
alert(selectedID);
this.setState({
doNothing: true
});
localStorage.setItem('SelectedSectionsChildID', selectedID);
}
handleNextBtn(e) {
this.setState({
doRedirect: true
});
}
handleBackBtn(e) {
this.setState({
doRedirectBack: true
});
}
render() {
let {doRedirect, doRedirectBack, Items} = this.state;
const FixedListFields = JSON.parse(("[" + (Items.substring(1, Items.length - 1)) + "]"));
console.log("===> " + FixedListFields);
if (doRedirect) {
return (<Redirect to={`/assessment/page6?id_token=${localStorage.getItem("tokenId")}`}/>)
} else if (doRedirectBack) {
return (<Redirect to={`/assessment/page4?id_token=${localStorage.getItem("tokenId")}`}/>)
} else {
return (
<div>
<Grid>
<Row style={{height: '100vh'}}>
{/*---------------------- Right Section --------------------------------*/}
<Col sm={8}>
{FixedListFields.map((item, i) => {
return <div key={i}>
<Row className="margin_h">
<Col sm={12}>
<h3 id="header1">{item.label}</h3>
</Col>
</Row>
<Childlist childItemHandler={this.childItemHandler} key={item.childIds} childIds={item.childIds}/>
</div>
})}
</Col>
</Row>
</Grid>
</div>
);
}
}
}
Childlist Component :
class Childlist extends React.Component {
constructor(props) {
super(props);
this.checkBoxHandler = this.checkBoxHandler.bind(this);
this.childItemHandler = this.childItemHandler.bind(this);
this.state = {
ChildItems: '',
selectedCheckboxes: true,
selectedSectionIds: [],
redirectToChildItemList: false,
};
}
componentWillMount = () => {
this.selectedCheckboxes = new Set();
};
componentDidMount() {
let selectedItemID = localStorage.getItem('SelectedTemplateID');
let itemIdList = (this.props.childIds + '').split(",");
request
.post(conn.GetItems)
.query({templateId: selectedItemID})
.send(itemIdList)
.set('Authorization', `Bearer ${localStorage.getItem("tokenId")}`)
.set('Content-Type', 'application/json-patch+json')
.set('accept', 'application/json')
.end((err, res) => {
this.setState({
ChildItems: res != null ? res.text : ""
});
});
}
childItemHandler(selectedID) {
localStorage.setItem('SelectedSectionsChildID', selectedID);
}
render() {
if (this.state.redirectToChildItemList) {
return (<Redirect to={`./page5?id_token=${localStorage.getItem("tokenId")}`}/>)
} else {
let {ChildItems} = this.state;
const FixedChildListFields = JSON.parse(("[" + (ChildItems.substring(1, ChildItems.length - 1)) + "]"));
const temp = this.state.selectedSectionIds + '';
let tempArray = temp.split(",");
return FixedChildListFields.map((childItem, i) => {
return <div>
<Row className="margin_m">
<Col sm={11}>
<div>
<div onClick={ () => this.props.childItemHandler(childItem.id) }
className="list_block" key={i}>
<span>{childItem.label} : </span>
<span>{childItem.description} </span>
{(childItem.childIds) ? (<img className=" align_vertical align_right"
src={arrow}
alt={'img'} width="40px" height="40px"/>) : ""
}
</div>
</div>
</Col>
</Row>
</div>
}
)
}
}
}
Related
I am writing a UI page to display a list of new messages, but I have gone wrong gin my code somewhere and not all of my page renders. I can't seem to find where.
My arrays correctly capture the data as I have tested this in console.log.
The text that should render on the page from the "displayNewMessage" doesn't work. Everything else renders.
Can anyone see where I have gone wrong please?
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
messageData: [],
newMessages: [],
isLoading: false
};
}
componentDidMount() {
this.setState({ isLoading: true });
this.setState({ userID: this.props.location.state });
const serachByUserId = this.props.location.state;
const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url =
"my-url" +
serachByColleagueId;
fetch(proxyurl + url)
.then((res) => res.json())
.then((data) => this.setState({ data: data }))
.then(
fetch(
proxyurl +
"my-URL"
)
.then((res) => res.json())
.then((messageData) =>
this.setState(
{ messageData: messageData, isLoading: false },
() => {}
)
)
);
}
render() {
const { data, isLoading, messageData } = this.state;
if (isLoading) {
return (
<div className="pageLoading">
<p>Loading...</p>
</div>
);
}
return (
<div>
<div>
<p>
<strong>
Welcome {data.Forename} {data.Surname}
</strong>
</p>
</div>
<div className="InfoBox">
<p>
<strong>Message Backlog</strong>
</p>
<p className="helpText">
The below Orders have open chats awaiting a response. Click on
the order number to send and view messages.
</p>
</div>
{this.getMessages(messageData)}
</div>
);
}
getMessages(messageData) {
var newMessagesArray = [];
var latestMessageIndex;
var latestMessage;
messageData.message_Subjects.forEach(saveNewMessage);
function saveNewMessage(sub) {
console.log(sub);
latestMessageIndex = sub.message_Chain.length - 1;
latestMessage = sub.message_Chain[latestMessageIndex];
if (latestMessage.sentFromId.toString().length === 7) {
var message_Chain = {
dateTime: latestMessage.dateTime,
sentFromId: latestMessage.sentFromId,
messagebody: latestMessage.messageBody,
messageChainId: latestMessage.messageChainId,
};
var message_subject = {
userId: sub.userId,
subjectId: sub.messageSubjectId,
subject: sub.subject,
message_Chain: message_Chain,
};
newMessagesArray.push({ message_Subject: message_subject });
} else {
// do nothing
}
}
if (newMessagesArray.length > 0) {
return ( <div>{newMessagesArray.forEach(displayNewMessage)}</div>)
} else {
return <p>No New messages.</p>;
}
function displayNewMessage(arrayItem) {
console.log(arrayItem);
return (
<div>
<ul>
<li>Subject = {arrayItem.message_Subject.subject}</li>
<li>Order number = {arrayItem.message_Subject.orderNumber}</li>
<li>Date and Time = {arrayItem.message_Subject.dateTime}</li>
<li>
message = {arrayItem.message_Subject.message_Chain.messageBody}
</li>
<li>
sent by = {arrayItem.message_Subject.message_Chain.sentFromId}
</li>
</ul>
</div>
);
}
}
}
export default Welcome;
Found away around this not rendering on the page with no need for the displayNewMessage function.
if (newMessagesArray.length > 0) {
return (
newMessagesArray = newMessagesArray.map((item) =>
<li key={item.id}>Subject = {item.message_Subject.subject}</li>
<li> etc..... </li>
))
} else {
return <p>No New messages.</p>;
};
I'm new to react, so forgive me. I'm having a problem understanding states, specifically those of children.
Purpose: I'm trying to create a form that a user can append more and more components -- in this case, images.
What happens: User appends 2 or more images. User tries to upload an image with UploadButton component, but both the images are the same. I believe this has to do with both appended children sharing the same state.
Question: How do I give each appended child its own image without affecting the other appended children?
class Page extends Component
constructor (props) {
super(props);
this.state = {
id: '',
numChildren: 0,
images: [],
}
this.onAddChild = this.onAddChild.bind(this);
}
showModal() {
this.setState({
numChildren: 0,
images: [],
});
}
renderModal()
const children = [];
//Here's my array of child components
for(var i = 0; i < this.state.numChildren; i += 1) {
children.push(<this.ChildComponent key={i} />);
}
return (
<ReactModal>
<this.ParentComponent addChild={this.onAddChild}>
{children}
</this.ParentComponent>
</ReactModal>
)
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
})
}
ParentComponent = (props) => (
<div>
{props.children}
<Button onClick={props.addChild}>Add Item</Button>
</div>
);
ChildComponent = () => (
<div>
<UploadButton
storage="menus"
value={this.state.images}
onUploadComplete={uri => this.setState({images: uri})}
/>
</div>
);
}
Here's the code for UploadButton:
import React, { Component } from 'react';
import uuid from 'uuid';
import firebase from '../config/firebase';
class UploadButton extends Component {
constructor(props) {
super(props);
this.state = {
isUploading: false
}
}
handleClick() {
const input = document.createElement("INPUT");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/gif, image/jpeg, image/png");
input.addEventListener("change", ({target: {files: [file]}}) => this.uploadFile(file));
input.click();
}
uploadFile(file) {
console.log('F', file);
const id = uuid.v4();
this.setState({ isUploading: true })
const metadata = {
contentType: file.type
};
firebase.storage()
.ref('friends')
.child(id)
.put(file, metadata)
.then(({ downloadURL }) => {
this.setState({ isUploading: false })
console.log('Uploaded', downloadURL);
this.props.onUploadComplete(downloadURL);
})
.catch(e => this.setState({ isUploading: false }));
}
render() {
const {
props: {
value,
style = {},
className = "image-upload-button",
},
state: {
isUploading
}
} = this;
return (
<div
onClick={() => this.handleClick()}
className={className}
style={{
...style,
backgroundImage: `url("${this.props.value}")`,
}}>
{isUploading ? "UPLOADING..." : !value ? 'No image' : ''}
</div>
);
}
}
export default UploadButton;
I tried to exclude all unnecessary code not pertaining to my problem, but please, let me know if I need to show more.
EDIT: This is my attempt, it doesn't work:
//altered my children array to include a new prop
renderModal() {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<this.ChildComponent imageSelect={this.onImageSelect} key={i} />);
}
//...
};
//my attempt to assign value and pass selected image back to images array
ChildComponent = () => (
<div>
<UploadButton
storage="menus"
value={uri => this.props.onImageSelect(uri)} //my greenness is really apparent here
onUploadComplete={uri => this.setState({images: uri})}
/>
//...
</div>
);
//added this function to the class
onImageSelect(uri) {
var el = this.state.images.concat(uri);
this.setState({
images: el
})
}
I know I'm not accessing the child prop correctly. This is the most complexity I've dealt with so far. Thanks for your time.
When you write this.state in Child / Parent component, you are actually accessing the state of Page. Now, I would recommend that you pass in the index of the child to the Child like so
children.push(<this.ChildComponent key={i} index={i}/>)
so that each children deals with only its own image like so
ChildComponent = ({index}) => (
<div>
<UploadButton
storage="menus"
value={this.state.images[index]}
onUploadComplete={uri => {
let images = this.state.images.slice()
images[index] = uri
this.setState({images})
}}
/>
</div>
);
I have search function where on entering text it returns the object from an array(json data) and based on the condition (whether it as an object or not) I need to show two different components ie. the list with matched fields and "No matched results found" component.
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
searchTextData: '',
isSearchText: false,
isSearchOpen: false,
placeholderText:'Search Content',
filteredMockData: [],
dataArray: []
};
}
handleSearchChange = (event, newVal) => {
this.setState({ searchTextData: newVal })
if (newVal == '') {
this.setState({ clearsearch: true });
this.setState({
filteredMockData: []
});
this.props.onDisplayCloseIcon(true);
} else {
this.props.onDisplayCloseIcon(false);
searchData.searchResults.forEach((item, index, array) => {
this.state.dataArray.push(item);
});
this.setState({ filteredMockData: this.state.dataArray });
}
}
clearInput = () => {
this.setState({ searchTextData: '' })
}
isSearchText = () => {
this.setState({ isSearchText: !this.state.isSearchText });
}
onSearchClick = () => {
this.setState({ isSearchOpen: !this.state.isSearchOpen });
this.setState({ searchTextData: '' });
this.props.onDisplayCloseIcon(true);
}
renderSearchData = () => {
const SearchDatasRender = this.state.dataArray.map((key) => {
const SearchDataRender = key.matchedFields.pagetitle;
return (<ResultComponent results={ SearchDataRender } /> );
})
return SearchDatasRender;
}
renderUndefined = () => {
return ( <div className = "search_no_results" >
<p> No Recent Searches found. </p>
<p> You can search by word or phrase, glossary term, chapter or section.</p>
</div>
);
}
render() {
return ( <span>
<SearchIcon searchClick = { this.onSearchClick } />
{this.state.isSearchOpen &&
<div className = 'SearchinputBar' >
<input
placeholder={this.state.placeholderText}
className= 'SearchInputContent'
value = { this.state.searchTextData}
onChange = { this.handleSearchChange }
/>
</div>
}
{this.state.searchTextData !== '' && this.state.isSearchOpen &&
<span className='clearText'>
<ClearIcon className='clearIcon' clearClick = { this.clearInput }/>
</span>
}
{this.state.searchTextData !== '' && this.state.isSearchOpen &&
<div className="SearchContainerWrapper">
<div className = "arrow-up"> </div>
<div className = 'search_result_Container' >
<div className = "search_results_title" > <span> Chapters </span><hr></hr> </div>
<div className="search_show_text" >
<ul className ="SearchScrollbar">
{this.state.filteredMockData.length ? this.renderSearchData() : this.renderUndefined() }
</ul>
</div>
</div>
</div>}
</span>
);
}
}
Search.propTypes = {
intl: intlShape.isRequired,
onSearchClick: PropTypes.func,
isSearchBarOpen: PropTypes.func,
clearInput: PropTypes.func,
isSearchText: PropTypes.func
};
export default injectIntl(Search);
Search is my parent component and based on the matched values I need to show a resultComponent like
class ResultComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
render(){
console.log(this.props.renderSearchData(),'Helloooo')
return(<p>{this.props.renderSearchData()}</p>)
}
}
ResultComponent.propTypes = {
results: PropTypes.string.isRequired
};
I'm getting an error "renderSearchData is not an function".I'm new to react and Hope someone can help.
The only prop passed to ResultComponent component is results
So in ResultComponent Component Replace
this.props.renderSearchData()
With
this.props.results
For this application, clicking a listed item once should create a button component underneath this listed item. Clicking the button should cause this listed item to be deleted.
I am currently facing difficulty trying to 'delete' the listed item after the button is clicked. Here is the code that went wrong (this is found in CountdownApp component) :
handleDelete(index) {
console.log('in handleDelete')
console.log(index)
let countdownList = this.state.countdowns.slice()
countdownList.splice(index, 1)
console.log(countdownList) // countdownList array is correct
this.setState({
countdowns: countdownList
}, function() {
console.log('after setState')
console.log(this.state.countdowns) // this.state.countdowns does not match countdownList
console.log(countdownList) // countdownList array is still correct
})
}
In the code above, I removed the item to be deleted from countdownList array with splice and tried to re-render the app with setState. However, the new state countdowns do not reflect this change. In fact, it returns the unedited state.
I have also tried the following:
handleDelete(index) {
this.setState({
countdowns: [] // just using an empty array to check if setState still works
}, function() {
console.log('after setState')
console.log(this.state.countdowns)
})
}
In the code above, I tried setting state to be an empty array. The console log for this.state.countdowns did not print out an empty array. It printed out the unedited state again
This is the only event handler that isn't working and I have no idea why (main question of this post) :/
If I have 'setstate' wrongly, why does the other 'setState' in other parts of my code work?? (I would like to request an in-depth explanation)
This is all my code for this app (its a small app) below:
import React from 'react'
import ReactDOM from 'react-dom'
class DeleteButton extends React.Component {
render() {
return (
<ul>
<button onClick={this.props.onDelete}>
delete
</button>
</ul>
)
}
}
class Countdown extends React.Component {
render () {
//console.log(this.props)
return (
<li
onClick={this.props.onClick}
onDoubleClick={this.props.onDoubleClick}
>
{this.props.title} - {this.props.days}, {this.props.color}
{this.props.showDeleteButton ? <DeleteButton onDelete={this.props.onDelete}/> : null }
</li>
)
}
}
const calculateOffset = date => {
let countdown = new Date(date)
let today = new Date
let timeDiff = countdown.getTime() - today.getTime()
let diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24))
return diffDays
}
class CountdownList extends React.Component {
countdowns() {
let props = this.props
// let onClick = this.props.onClick
// let onDoubleClick = this.props.onDoubleClick
let rows = []
this.props.countdowns.forEach(function(countdown, index) {
rows.push(
<Countdown
key={index}
title={countdown.title}
days={calculateOffset(countdown.date)}
color={countdown.color}
showDeleteButton={countdown.showDeleteButton}
onDelete={() => props.onDelete(index)}
onClick={() => props.onClick(index)}
onDoubleClick={() => props.onDoubleClick(index)}
/>
)
})
return rows
}
render() {
return (
<div>
<ul>
{this.countdowns()}
</ul>
</div>
)
}
}
class InputField extends React.Component {
render() {
return (
<input
type='text'
placeholder={this.props.placeholder}
value={this.props.input}
onChange={this.props.handleInput}
/>
)
}
}
class DatePicker extends React.Component {
render() {
return (
<input
type='date'
value={this.props.date}
onChange={this.props.handleDateInput}
/>
)
}
}
class CountdownForm extends React.Component {
constructor(props) {
super(props)
this.state = {
title: this.props.title || '',
date: this.props.date || '',
color: this.props.color || ''
}
}
componentWillReceiveProps(nextProps) {
this.setState({
title: nextProps.title || '',
date: nextProps.date || '',
color: nextProps.color || ''
})
}
handleSubmit(e) {
e.preventDefault()
this.props.onSubmit(this.state, this.reset())
}
reset() {
this.setState({
title: '',
date: '',
color: ''
})
}
handleTitleInput(e) {
this.setState({
title: e.target.value
})
}
handleDateInput(e) {
this.setState({
date: e.target.value
})
}
handleColorInput(e) {
this.setState({
color: e.target.value
})
}
render() {
return (
<form
onSubmit={(e) => this.handleSubmit(e)}
>
<h3>Countdown </h3>
<InputField
placeholder='title'
input={this.state.title}
handleInput={(e) => this.handleTitleInput(e)}
/>
<DatePicker
date={this.state.date}
handleDateInput={(e) => this.handleDateInput(e)}
/>
<InputField
placeholder='color'
input={this.state.color}
handleInput={(e) => this.handleColorInput(e)}
/>
<button type='submit'>Submit</button>
</form>
)
}
}
class CountdownApp extends React.Component {
constructor() {
super()
this.state = {
countdowns: [
{title: 'My Birthday', date: '2017-07-25', color: '#cddc39', showDeleteButton: false},
{title: 'Driving Practice', date: '2017-07-29', color: '#8bc34a', showDeleteButton: false},
{title: 'Korean BBQ', date: '2017-08-15', color: '#8bc34a', showDeleteButton: false}
]
}
}
handleCountdownForm(data) {
if (this.state.editId) {
const index = this.state.editId
let countdowns = this.state.countdowns.slice()
countdowns[index] = data
this.setState({
title: '',
date: '',
color: '',
editId: null,
countdowns
})
} else {
data.showDeleteButton = false
const history = this.state.countdowns.slice()
this.setState({
countdowns: history.concat(data),
})
}
}
handleDelete(index) {
console.log('in handleDelete')
console.log(index)
let countdownList = this.state.countdowns.slice()
countdownList.splice(index, 1)
console.log(countdownList)
this.setState({
countdowns: countdownList
}, function() {
console.log('after setState')
console.log(this.state.countdowns)
})
}
handleCountdown(index) {
const countdownList = this.state.countdowns.slice()
let countdown = countdownList[index]
countdown.showDeleteButton = !countdown.showDeleteButton
this.setState({
countdowns: countdownList
})
}
handleDblClick(index) {
const countdownList = this.state.countdowns
const countdown = countdownList[index]
this.setState({
title: countdown.title,
date: countdown.date,
color: countdown.color,
editId: index
})
}
render() {
return (
<div>
<CountdownForm
title={this.state.title}
date={this.state.date}
color={this.state.color}
onSubmit={(data) => {this.handleCountdownForm(data)}}
/>
<CountdownList
countdowns={this.state.countdowns}
onDelete={(index) => this.handleDelete(index)}
onClick={(index) => this.handleCountdown(index)}
onDoubleClick={(index) => this.handleDblClick(index)}
/>
</div>
)
}
}
ReactDOM.render(
<CountdownApp />,
document.getElementById('app')
)
I managed to find the answer to my own question!
setState worked as expected. The bug was due to <li> container that wrapped the event handler.
Clicking <li> causes it to call onClick event (which is managed by handleCountdown function in CountdownApp component) which causes it to setState.
As the delete button was wrapped in <li> container, clicking the delete button calls 2 event listeners - handleCountdown and handleDelete. handleCountdown is called twice in this case, once from clicking <li> to expand and the next call when the delete button is clicked.
There is a high chance that the last async setState dispatched from handleCountdown overwrites handleDelete's setState. Hence, the bug.
Here is changes: (I recoded everything again so the names might differ a little but the logic stays the same)
class Countdown extends React.Component {
render () {
return (
<li>
<div onClick={this.props.onClick} > // Add this div wrapper!
{this.props.title} - {this.props.days}, {this.props.color}
</div>
{this.props.toShow ?
<ButtonsGroup
onDelete={this.props.onDelete}
onEdit={this.props.onEdit}
/>
: null}
</li>
)
}
}
So the solution is to separate the clickable area and the buttons. I added a div wrapper over the text in <li> so whenever the text in <li> is clicked, the added <ul> will be out of onClick event handler area.
I have a small exercise I'm working on to learn ReactJS. I'm making an API call to display a list from iTunes, and when the individual list items are clicked on, they move over to a new list (kind of like a queue). When clicked on in the new list, they move back to the original list, however the list item is not being removed from the new list when it gets moved back. Here is a jsfiddle of my problem: https://jsfiddle.net/6k9ncbr6/
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
moveResults: []
}
this.handleEvent = this.handleEvent.bind(this);
}
showResults = (response) => {
this.setState({
searchResults: response.results,
moveResults: []
})
}
search = (URL) => {
$.ajax({
type: 'GET',
dataType: 'json',
url: URL,
success: function(response) {
this.showResults(response);
}.bind(this)
});
}
handleEvent = (clickedTrack) => {
const { searchResults, moveResults } = this.state;
const isInSearchResults = searchResults.some(result => result.trackId === clickedTrack.trackId);
this.setState({
searchResults: isInSearchResults ? searchResults.filter(i => i.trackId !== clickedTrack.trackId) : [...searchResults, clickedTrack],
moveResults: isInSearchResults ? [...moveResults, clickedTrack] : moveResults.filter(i => i.trackId !== clickedTrack)
});
}
componentDidMount() {
this.search('https://itunes.apple.com/search?term=broods');
}
render(){
return (
<div>
<Results
searchResults={this.state.searchResults}
handleEvent={this.handleEvent}/>
<Results
searchResults={this.state.moveResults}
handleEvent={this.handleEvent} />
</div>
);
}
}
class Results extends React.Component {
render(){
const { handleEvent, searchResults } = this.props;
return(
<ul>
{this.props.searchResults.map((result, idx) =>
<ResultItem
key={`${result.trackId}-${idx}`}
trackName={result.trackName}
track={result}
handleClick={handleEvent} />
)}
</ul>
);
}
}
class ResultItem extends React.Component {
render(){
const { handleClick, track, trackName } = this.props
return <li onClick={() => handleClick(track)}> {trackName} </li>;
}
}
ReactDOM.render(
<App />, document.getElementById('root')
);
You left off a property name:
moveResults: isInSearchResults ? [...moveResults, clickedTrack] : moveResults.filter(i => i.trackId !== clickedTrack.trackId)
The clickedTrack.trackId is missing the .trackId in your existing code.
Here's the working jsFiddle.