In my app i have an initial state in a component App.js it's an array of objects
Here is App.js code:
import React, { Component } from 'react';
import './App.css';
import { render } from '#testing-library/react';
// Import Used Components
import SearchBar from '../SearchBar/SearchBar';
import Playlist from '../PlayList/PlayList';
import SearchResults from '../SearchResults/SearchResults';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [{name: 'name1',artist: 'artist1',album: 'album1',id: 1},
{name: 'name2',artist: 'artist2',album: 'album2',id: 2}]
};
}
// Adding JSX to App Component
render() {
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults} />
<Playlist />
</div>
</div>
</div>
);
}
}
export default App;
I passed this initial state as a prop called searchResults to another component named .
Here is searchResults.js code :
import './SearchResults.css';
import TrackList from '../TrackList/TrackList';
class SearchResults extends React.Component {
render() {
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList tracks={this.props.searchResults}/>
</div>
);
}
}
export default SearchResults;
then I used passed this prop to another component called TrackList
here is TrackList.js code:
import React from 'react';
import './TrackList.css';
import Track from '../Track/Track';
class TrackList extends React.Component {
render() {
return(
<div className="TrackList">
{
this.props.tracks.map(track => {
return <Track track={track} key={track.id} />;
} )
}
</div>
);
}
}
export default TrackList;
In Track.js I want to map through this initial state array to render a component called Track
here is the Track.js code:
import React from 'react';
import './Track.css';
class Track extends React.Component {
renderAction() {
if (this.props.isRemoval){
return <botton className='Track-action'>-</botton>;
} else {
return <botton className='Track-action'>+</botton>;
}
};
render() {
return (
<div className="Track">
<div className="Track-information">
<h3>{this.props.track.name}</h3>
<p>{this.props.track.artist} | {this.props.track.album}</p>
</div>
<button className="Track-action">{this.renderAction}</button>
</div>
);
}
}
export default Track;
But something is wrong !! I keep getting this error:
TypeError: Cannot read property 'map' of undefined
Here is searchBar.js component code:
import React from 'react';
import './SearchBar.css';
class SearchBar extends React.Component {
render() {
return (
<div className="SearchBar">
<input placeholder="Enter A Song, Album, or Artist" />
<button className="SearchButton">SEARCH</button>
</div>
);
}
}
export default SearchBar;
HERE LINK TO THE PROJECT WITH THE SAME ERROR ON SANDBOX
https://codesandbox.io/s/upbeat-dawn-lwbxb?fontsize=14&hidenavigation=1&theme=dark
Change your TrackList component to this:
class TrackList extends React.Component {
render() {
return (
<div className="TrackList">
{this.props.tracks && this.props.tracks.map(track => {
return <Track key={track.id} track={track}/>
})}
</div>
);
}
}
You can't map through this.props.tracks if it is undefined.
The && (AND operator) is a concise way to conditionally render in React. You can think of it like a simple if statement: If the expression on the left is true, then do x.
I'll also expand on why the this.props.tracks was undefined in a certain instance in your case.
The reason that this problem is happening is your Playlist component. If you uncomment this component from your App you will notice your original code will work.
This is because your PlayList component, like your SearchResults component, also renders your TrackList component. The problem is you haven't passed your state and props down to TrackList like you did with your SearchResults component.
So an alternative solution would be to pass your state and props down from PlayList to TrackList:
App.js
// ...
<SearchResults searchResults={this.state.searchResults} />
<Playlist searchResults={this.state.searchResults}/>
// ...
PlayList.js
// ...
<TrackList tracks={this.props.searchResults}/>
// ...
Related
so ,i have hard coded the state of the parent component and after passing it in the child component i am unable to retrieve it the child component.
and on the other hand if i pass any other other prop other than the state it works.
this is the child component:
import React from 'react';
import './searchresults.css'
class SearchResults extends React.Component {
render(){
let searchresults= this.props.searchresults;
return(
<div className= "searchresults">
<h2>Weather</h2>
<h3>Temprature:{searchresults.main.temp} </h3>
<h3>Temperature minimum: 25 degrees</h3>
<h3>Temperature maximum: 40 degrees</h3>
<h3>Humidity: 81% </h3>
</div>
)
}
}
export default SearchResults;
this is the parent component:
import React from 'react';
import './App.css';
import SearchBar from '../searchbar/searchbar'
import SearchResults from '../SearchResults/searchresults'
class App extends React.Component {
constructor(props){
super(props)
this.state = {
searchresults:[{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":300,"main":"Drizzle","description":"light intensity drizzle","icon":"09d"}],"base":"stations",
"main":{"temp":280.32,"pressure":1012,"humidity":81,"temp_min":279.15,"temp_max":281.15
},"visibility":10000,"wind":{"speed":4.1,"deg":80},"clouds":{"all":90},"dt":1485789600,"sys":{"type":1,"id":5091,"message":0.0103,"country":"GB","sunrise":1485762037,"sunset":1485794875},"id":2643743,"name":"London","cod":200}]
}
}
render(){
return (
<div className="App">
<header className="App-header">
<h1>Wanderer</h1>
</header>
<SearchBar />
<SearchResults searchresults ={this.state.searchresults}
/>
</div>
);
}
}
export default App;
and this the error i get:
TypeError: Cannot read property 'temp' of undefined
SearchResults.render
F:/rishit/wanderer/src/components/SearchResults/searchresults.js:18
Since searchresults in the parent with one item you could pass it like :
<SearchResults searchresults ={this.state.searchresults[0]} />
searchResults is an arrray it should be searchresults[0].main.temp
<h3>Temprature:{searchresults[0].main.temp} </h3>
Here, searchresults is an array. You need to provide index to access an element.
You can use searchresults[0].main.temp but that is not the best way and not a solution if searchresults had multiple elements. I would do it in the following way.
import React from 'react';
import './searchresults.css'
class SearchResults extends React.Component {
render(){
return(
this.props.searchresults.map((searchResult) => (
<div className= "searchresults">
<h2>Weather</h2>
<h3>Temprature:{searchResult.main.temp}</h3>
<h3>Temperature minimum: 25 degrees</h3>
<h3>Temperature maximum: 40 degrees</h3>
<h3>Humidity: 81% </h3>
</div>
))
)
}
}
I hope it works.
I am getting "TypeError: undefined is not an object (evaluating 'this.props.tracks.map')" in the browser, although app compiles successfully.
I checked with console.log that both SearchResults and TrackList receive props (console.log prints all elements of array within map() method) so I do not understand why the object is undefined.
Code is presented below:
File App.js
import React from 'react';
import SearchResults from '../SearchResults/SearchResults.js'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [{name: '1'}, {name: '2'}]
}
}
render() {
return (
<div>
<div class="App">
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults} />
</div>
</div>
</div>
)
}
}
export default App;
File SearchResults.js
import React from 'react';
import TrackList from '../TrackList/TrackList.js'
class SearchResults extends React.Component {
render() {
return (
<div className="SearchResults">
<TrackList tracks={this.props.searchResults}/>
</div>
)
}
}
export default SearchResults;
File TrackList.js
import React from 'react';
import Track from '../Track/Track.js'
class TrackList extends React.Component {
render() {
return (
<div className="TrackList">
{
this.props.tracks.map(track => {
return <Track track={track}/>
})
}
</div>
)
}
}
export default TrackList;
Update
I also added error from the browser:
Not exactly sure why this is happening, but when console logging this.props.tracks in Tracklist it is the defined, passed array from SearchResults then becomes undefined, thus causing the error you see.
QUICK FIX Use Guard Pattern
class TrackList extends React.Component {
render() {
const { tracks } = this.props;
console.log(tracks); // two logs, first (2)[{...}, {...}], then undefined
return (
<div className="TrackList">
{
tracks && tracks.map((track, index) => { // Use a guard here to handle undefined tracks prop
return <Track key={index} track={track} />
})
}
</div>
)
}
}
In the above snippet you can simply place a guard on the tracks prop to defend against undefined values for the mapping. The more important question though is why this is occurring since the prop is never undefined here in SearchResult:
class SearchResults extends React.Component {
render() {
console.log('SearchResults', this.props.searchResults); // defined, only see 1 log entry
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList tracks={this.props.searchResults} />
</div>
)
}
}
FURTHER INVESTIGATION
I looked over the rest of your code and found that App is rendering PlayList, and PlayList also renders a TrackList but this time passes no tracks!
class Playlist extends React.Component {
render() {
return (
<div className="Playlist">
<input defaultValue={'New Playlist'} />
<TrackList /> // tracks becomes undefined here
<button className="Playlist-save">SAVE TO SPOTIFY</button>
</div>
)
}
}
SOLUTION Define Props/DefaultProps
At this point I would recommend using the react prop-types package to define your component's props and provide default values in some cases. Your TrackList component becomes:
import React from 'react';
import PropTypes from 'prop-types'; // import proptypes
import Track from '../Track/Track.js'
import './TrackList.css'
const propTypes = {
tracks: PropTypes.array // If you make this required (i.e. PropTypes.array.isRequired) then you'll get a react warning about missing props
};
const defaultProps = {
tracks: [], // if tracks props is unspecified this value is used
};
class TrackList extends React.Component {
render() {
const { tracks } = this.props;
console.log(tracks);
return (
<div className="TrackList">
{
tracks.map((track, index) => { // no longer need guard
return <Track key={index} track={track} />
})
}
</div>
)
}
}
TrackList.propTypes = propTypes;
TrackList.defaultProps = defaultProps;
export default TrackList;
I have two component in my project one is Tag and the other is LandingTicker so i want when i click Tag componet update state for LandTicker componet, and landticker componet in different file.
how i can do that?
thank you.
Tag component code::
tag/index.js
import React from 'react';
import './index.scss';
class Tag extends React.Component {
handleClick(e) {
let tags = document.querySelectorAll('.show-clickable');
Array.from(tags).map(el => el.classList.remove('selected-tag'))
e.target.classList.add('selected-tag');
/*
Here i should update the state for LandingTicker component.
and remember any component in different file.
How i can do that???
*/
}
render() {
return (
<div
className="show-clickable"
onClick={this.handleClick}
>
click here
</div>
);
}
}
export default Tag;
LandingTicker component code::
LandingTicker/index.js
import React from 'react';
import TickerRow from './TickerRow';
import './index.scss';
class LandingTicker extends React.Component {
state = {
coin: 'USD'
}
render() {
return (
<div className="landing-ticker__body">
{selectCoin(this.state.coin)}
</div>
</div>
);
}
}
const selectCoin = (coin) => {
const coins = {
USD: ['BTCUSD', 'ETHUSD', 'EOSUSD', 'LTCUSD'],
EUR: ['BTCEUR', 'ETHEUR', 'EOSEUR'],
GBP: ['BTCGBP', 'EOSGBP'],
JPY: ['BTCJPY', 'ETHJPY'],
};
return (
coins[coin].map(el =>
<TickerRow symbol={el} key={el.toString()} />
)
);
}
export default LandingTicker;
Edit:
my component Hierarchy::
StatusTable
TagsTable
Tag
TickerSearch
LandingTickers
TickersRow
StatusTable component code::
import React from 'react';
import TagsTable from './TagsTable';
import TickerSearch from './TickerSearch';
import LandingTicker from './LandingTicker';
import './StatusTable.scss';
class StatusTable extends React.Component {
render() {
return (
<div className="status-table">
<TagsTable />
<TickerSearch />
<LandingTicker />
</div>
);
}
}
export default StatusTable;
React handle all its component data in the form of state and props(immutable). So it is easy to pass data from parent to child or one component to another using props :
Your Tag.js file:
import React, { Component } from "react";
import LandingTicker from "./LandTicker";
class Tag extends Component {
constructor(props) {
super(props);
this.state = {
trigger: true
};
}
handleClick(e) {
// do all logic here and set state here
this.setState({ trigger: this.state.trigger });
}
render() {
//And then pass this state here as a props
return (
<div className="show-clickable" onClick={this.handleClick}>
click here
<LandingTicker trigger={this.state.trigger} />
</div>
);
}
}
export default Tag;
Inside LandTicker.js file:
import React from 'react';
import TickerRow from './TickerRow';
import './index.scss';
class LandingTicker extends React.Component {
state = {
coin: 'USD'
}
render() {
//Catch your props from parent here
//i.e this.props(it contains all data you sent from parent)
return (
<div className="landing-ticker__body">
{selectCoin(this.state.coin)}
</div>
);
}
}
const selectCoin = (coin) => {
const coins = {
USD: ['BTCUSD', 'ETHUSD', 'EOSUSD', 'LTCUSD'],
EUR: ['BTCEUR', 'ETHEUR', 'EOSEUR'],
GBP: ['BTCGBP', 'EOSGBP'],
JPY: ['BTCJPY', 'ETHJPY'],
};
return (
coins[coin].map(el =>
<TickerRow symbol={el} key={el.toString()} />
)
);
}
export default LandingTicker;
I think this is the best answer for your question if you don't use state management system such as Redux or Mobx.
https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
(you need to check third option)
I want to pass a value to a div with id good in my index.html but it brings this error, Objects are not valid as a React child (found: [object HTMLDivElement]). If you meant to render a collection of children, use an array instead. in TestComponent (at App.js:49)
in div (at App.js:28)
in Apps (at index.js:7)
Please what am I doing wrong
TestComponent.js
import React, { Component } from 'react';
class TestComponent extends Component {
componentDidMount(){
console.log("Great");
}
render() {
// var {test} = this.props;
return (
<p>
{this.props.test}
</p>,
document.getElementById("good")
);
}
}
export default TestComponent;
App.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import TestComponent from "./components/TestComponent"
class Apps extends Component {
constructor(props){
super(props);
}
render() {
return (
<div>
<TestComponent test='doyin'/>
</div>
);
}
}
export default Apps;
Index.html
<div id="good"></div>
A class Component render function shouldn't use document.getElementById, you need to use ReactDOM.render to do that
import React, { Component } from 'react';
class TestComponent extends Component {
componentDidMount(){
console.log("Great");
}
render() {
// var {test} = this.props;
return (
<p>
{this.props.test}
</p>
);
}
}
export default TestComponent;
App
class Apps extends Component {
constructor(props){
super(props);
}
render() {
return (
<div>
<TestComponent test='doyin'/>
</div>
);
}
}
ReactDOM.render(<Apps />, document.getElementById("good"))
export default Apps;
In TestComponent.js, inside render function you are trying to return two elements, <p> and document.getElementById("good"). Probably you just wanted to return <p>:
render() {
return <p>{this.props.test}</p>;
}
Also, it looks like you've mistaken React.Component.render with ReactDOM.render(element, container[, callback]) where the second argument of the functions is the container.
I am not sure why I am getting TypeError: Cannot read property 'map' of undefined here:
I am trying to pass an array down from App.js to => SearchResult.js to => TrackList.js
TrackList.js:
import React from 'react';
import Track from '../Track/Track'
import '../TrackList/TrackList.css';
class TrackList extends React.Component {
render() {
return (
<div className="TrackList">
{this.props.tracks.map(track => {
console.log(track);
<Track track={track} />}
)}
</div>
);
}
}
export default TrackList;
the console.log(track) above returns an array of objects as expected, I think that makes sure that this.props.tracks is an array.
App.js:
import React, { Component } from 'react';
import './App.css';
import SearchBar from './Components/SearchBar/SearchBar';
import SearchResults from './Components/SearchResults/SearchResults';
import Playlist from './Components/Playlist/Playlist';
const track = {
name: "Tiny Dancer",
artist: 'Elton John',
album: 'Madman Across The Water'
};
const tracks = [track, track, track];
class App extends Component {
constructor (props) {
super(props);
this.state = {'searchResults': tracks};
}
render() {
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults}/>
<Playlist />
</div>
</div>
</div>
);
}
}
export default App;
SearchResults.js:
import React from 'react';
import '../SearchResults/SearchResults.css';
import TrackList from '../TrackList/TrackList';
class SearchResults extends React.Component {
render() {
return (
<div className="SearchResults">
{/* {console.log(this.props.searchResults)} */}
<h2>Results</h2>
<TrackList tracks={this.props.searchResults}/>
</div>
);
}
}
export default SearchResults;
I also tried using props to pass the array from App.js (instead of state) but I got the same error.
sometimes react will render the app before your props loaded. this especially happens if you're getting props form an api call. react will throw an error if that happens. here's a work around:
import React from 'react';
import Track from '../Track/Track'
import '../TrackList/TrackList.css';
class TrackList extends React.Component {
constructor(props){
super(props)
this.state={}
}
render() {
//es6 syntax. this will allow the app to render and then replace the
//tracks variable with this.props.tracks when the props are ready
//this check if this.props.tracks exist and if it doesn't it will
//assign the variable to an empty array. then .map will not be
//called on an undefined variable.
let tracks = this.props.tracks ? this.props.tracks : [];
return (
<div className="TrackList">
{tracks.map(track => {
console.log(track);
<Track track={track} />}
)}
</div>
);
}
}
export default TrackList;