Unable to pass value of props in React component - reactjs

I am trying to iterate over a list of json values and create cards based on that. I tried many approaches but feel I am missing something while accessing props in my components.
Below is my JSON
let mockdata = {values : [{name : 'John'}, {name: 'Mike'}, {name : 'Sam'}]};
This is how my app.js looks like. I am importing cardcontainer in the app.
class App extends Component {
state = {
items: []
}
componentDidMount() {
fetch(endpoint)
.then(response => response.json())
.then(({values : items}) => this.setState({items}))
}
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Ping Pong Time Table</h1>
</header>
<div className="card-container">
<CardContainer/>
</div>
</div>
);
}
}
export default App;
This how my cardcontainer looks like
class CardBody extends React.Component {
render() {
return (
<div className="card-body">
<p className="date">March 20 2015</p>
<h2>{this.props.title}</h2>
</div>
)
}
}
class Card extends React.Component {
render() {
return (
<article className="card">
<CardBody title={this.props.value}/>
</article>
)
}
}
class CardContainer extends React.Component {
render(){
var info = this.props.items;
console.log(info);
var elements = [];
for(let val in info){
elements.push(<Card value={ info[name] } />);
}
return (
<div>
{elements}
</div>
);
}
}
export default CardContainer;
I have tried different approaches about this but somehow console.log(info) in cardcontainer gives undefined. Any help or pointers is greatly appreciated.
Thanks

You are not passing any properties to your CardContainer. To pass properties to a React component, you pass them like this:
class App extends Component {
state = {
items: []
}
componentDidMount() {
fetch(endpoint)
.then(response => response.json())
.then(({values : items}) => this.setState({items}))
}
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Ping Pong Time Table</h1>
</header>
<div className="card-container">
<CardContainer items={this.state.items} />
</div>
</div>
);
}
}
export default App;
You can pass as many properties as you like. These properties are available to the components on its this.props.

You have pass this.state.items as props to CardContainer and in CardContainer pass item.name to Card component
class App extends React.Component {
constructor() {
super();
this.state = {
items: [{name : 'John'}, {name: 'Mike'}, {name : 'Sam'}]
}
}
render(){
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Ping Pong Time Table</h1>
</header>
<div className="card-container">
<CardContainer items = {this.state.items} />
</div>
</div>
);
}
}
CardContainer
class CardContainer extends React.Component {
render(){
var info = this.props.items;
console.log(info);
var elements = info.map((item) => (<Card value={item.name} />));
return (
<div>
{elements}
</div>
);
}
}
The working code is available in https://codesandbox.io/s/woz4v2nxmw

Related

TypeError: Cannot read properties of undefined (reading 'state')

I tried console.log(this.state.bike). It gave me an array that basically contains everything I need. But when I try to access them I get undefined. How do I access the bikeLocation and bikeDescription? I have added 3 files. BikeServices, ListBikes and ViewBike Components.
import React, { Component } from 'react';
import BikeServices from '../services/BikeServices';
class viewBike extends Component {
constructor(props){
super(props)
this.state = {
bikeId: this.props.match.params.bikeId,
bike: {}
}
}
componentDidMount(){
BikeServices.getBikesById(this.state.bikeId).then( (res) => {
this.setState({bike: res.data});
//console.log(this.state.bike);
console.log(this.state.bike.bikeLocation);
});
}
render() {
return (
<div>
<br></br>
{this.state.bikeId}
<br></br>
{this.state.bike.bikeLocation}
</div>
);
}
}
export default viewBike;
//BikeServices
import axios from 'axios';
const BIKE_URL = `http://localhost:8090/api/v1/bikes`;
class BikeService{
getBikes(){
return axios.get(BIKE_URL);
}
getBikesById(bikeId){
return axios.get(BIKE_URL+ '/' +bikeId);
}
}
export default new BikeService();
//ListBikes component
import React, { Component } from 'react';
import BikeService from '../services/BikeServices'
class ListBikes extends Component {
constructor(props) {
super(props)
this.state = {
bikes : [],
}
this.viewBike = this.viewBike.bind(this);
}
// bikes=[]
componentDidMount(){
BikeService.getBikes().then((res) => {
this.setState({ bikes: res.data});
});
}
viewBike(bikeId){
console.log(this.props)
this.props.history.push(`/bike/${bikeId}`);
}
render() {
return (
<div className='container'>
{
this.state.bikes.map(
bike=>
<div className="card">
<div className="card-header">
<img src="https://c0.wallpaperflare.com/preview/483/210/436/car-green-4x4-jeep.jpg" alt="rover" />
</div>
<div className="card-body">
<span className="tag tag-teal">{bike.bikeStatus}</span>
<h4>
Bike Category: {bike.bikeCategory}
</h4>
<p>
Bike Description: {bike.bikeDescription}
<br></br>
Location:
{bike.location.address}, {bike.location.city}, {bike.location.state}, {bike.location.zip}
</p>
<button style={{marginLeft: "10px"}} onClick={() => this.viewBike(bike.bikeId)} className='btn btn-primary'>View</button>
<button>Rent</button>
</div>
</div>
)
}
</div>
);
}
}
export default ListBikes;
I tried console.log(this.state.bike). It gave me an array that basically contains everything I need. But when I try to access them I get undefined. How do I access the bikeLocation and bikeDescription? I have added 3 files. BikeServices, ListBikes and ViewBike Components.

Single JSON Object read Title render method issue in React.js

I have following code which render an console single Object json. When I try to render title or content it gives me an error.
import React, { Component } from 'react'
import axios from 'axios';
export default class Single extends Component {
constructor(props) {
super(props)
this.state = {
postData :[]
}
}
componentDidMount() {
axios.get(`https://example.co/wp-json/wp/v2/company/2575`)
.then(res => {
const postData = res.data;
this.setState({ postData });
})
}
render() {
return (
<div>
{/* {console.log(this.state.postData)} */}
<section className="sf-detail-page">
<div className="container">
<div className="row">
<div className="col-md-10 detail main">
<div className="row detail-page-compnay-content">
<div className="col-md-12">
{console.log(this.state.postData)}
<h1 key={this.state.postData.id}>{this.state.postData.id}</h1>
<h2 key={this.state.postData.id}>{this.state.postData.title.rendered}</h2>
)
}
}
{"id":2575,"date":"2020-08-22T04:53:04","date_gmt":"2020-08-22T04:53:04","title":{"rendered":"Vasim"},"content":{"rendered":"This is demo"}}
Update Code :
import React, { Component } from 'react'
import axios from 'axios';
export default class Single extends Component {
constructor(props) {
super(props)
this.state = {
postData :Object
}
}
componentDidMount() {
axios.get(`https://example.co/wp-json/wp/v2/company/2575`)
.then(res => {
const postData = res.data;
this.setState({ postData });
})
}
render() {
if (!this.state.postData) {
return(
<div>Loading...</div>
)
}
return (
<div>
{/* {console.log(this.state.postData)} */}
<section className="sf-detail-page">
<div className="container">
<div className="row">
<div className="col-md-10 detail main">
<div className="row detail-page-compnay-content">
<div className="col-md-12">
{console.log(this.state.postData)}
<h1 key={this.state.postData.id}>{this.state.postData.id}</h1>
<h2 key={this.state.postData.id}>{this.state.postData.title.rendered}</h2>
</div></div></div></div></div></section>
</div>
)
}
}
this.state.postData is an empty array on first render and you're trying to access this.state.postData.title.rendered, which will not work.
Change initial state to null instead of []
Add a condition before rendering the data to check if it's loaded yet. Something like:
if(!this.state.postData) return "loading..."

Change CSS by passing data between siblings

I have two child components. The first child is an image and the second child is a search input. When I type something in the input field, I want the image to hide itself. The passing of data from the second child to the parent goes well. But the first child still appears...
Parent:
class Main extends React.Component {
constructor() {
super();
this.state = {
displayValue: 'block'
};
}
hideImage = () => () => {
alert('You pressed a key, now the apple should be gone')
this.setState ({
displayValue: 'none'
});
};
render() {
return (
<div>
<Image />
<Search hideImage={this.hideImage()}/>
</div>
);
}
}
ReactDOM.render(<Main />, document.getElementById('root'));
First Child:
export default class Image extends Component {
render() {
return (
<div>
<img style={{display : this.props.displayValue}} src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTAxoq2YSBjoS0Lo3-zfqghoyNzZ9jHxoOc5xuFBoopMtKP6n4B"></img>
</div>
)
}
}
Second Child:
export default class Search extends Component {
render() {
return (
<div>
<input onInput={this.props.hideImage} placeholder="Search someting"></input>
</div>
)
}
}
You have to pass the displayValue state into your Image component as a prop. Also you have to pass the hideImage function without initializing it using the two brackets. The below code should work for you.
class Main extends React.Component {
constructor() {
super();
this.state = {
displayValue: 'block'
};
}
hideImage = () => () => {
alert('You pressed a key, now the apple should be gone')
this.setState ({
displayValue: 'none'
});
};
render() {
return (
<div>
<Image displayValue={this.state.displayValue}/>
<Search hideImage={this.hideImage}/>
</div>
);
}
}
You just had a couple of typos.
class Image extends React.Component {
render() {
return (
<div>
<img
style={{ display: this.props.displayValue }}
src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTAxoq2YSBjoS0Lo3-zfqghoyNzZ9jHxoOc5xuFBoopMtKP6n4B"
alt="altprop"
/>
</div>
);
}
}
// Second Child:
class Search extends React.Component {
render() {
return (
<div>
<input onInput={this.props.hideImage} placeholder="Search someting" />
</div>
);
}
}
class Main extends React.Component {
constructor() {
super();
this.state = {
displayValue: "block"
};
}
hideImage(e) {
e.preventDefault();
alert("You pressed a key, now the apple should be gone");
this.setState({
displayValue: "none"
});
};
render() {
return (
<div>
<Image displayValue={this.state.displayValue}/>
<Search hideImage={this.hideImage.bind(this)} />
</div>
);
}
}
Call your Image component like this
<Image displayValue={this.state.displayValue} />
It should then already work, but here is a shorter way to write your code.
// First Child:
const Image = ({displayValue}) => <div>
<img alt='' style={{display : displayValue}} src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTAxoq2YSBjoS0Lo3-zfqghoyNzZ9jHxoOc5xuFBoopMtKP6n4B"></img>
</div>

ReactJS - Props and classes

I am really new to React, so I am trying to build a Pokemon app. My main goal is to build the list of 20 pokemon, and detail box which when clicked on Pokemon from the list should display chosen pokemon details, pictures etc.
import React, { Component } from 'react';
import './styles/App.css';
class App extends Component{
render(){
return <div className="App">
<h1> Pokedex! </h1>
<PokemonList/>
<PokemonDetail/>
</div>;
}
}
class Pokemon extends Component {
render(){
const {pokemon,id} = this.props;
return <div className="pokemon--species">
<button className="pokemon--species--container">
<div className="pokemon--species--sprite">
<img src={`https://pokeapi.co/media/sprites/pokemon/${id}.png`} />
</div>
<div className="pokemon--species--name">{id} {pokemon.name} {pokemon.url} </div>
</button>
</div>;
}
}
class PokemonDetail extends Component {
render(){
const {pokemon, id} = this.props;
return <div className="pokemon--species">
<button className="pokemon--species--container">
<div className="pokemon--species--sprite">
<img src={`https://pokeapi.co/media/sprites/pokemon/${id}.png`} />
</div>
<div className="pokemon--species--name">{id}</div>
<p>Attack:72</p>
<p>Defense:23</p>
<p>Health:99</p>
</button>
</div>;
}
}
class PokemonList extends Component{
constructor(props){
super(props);
this.state = {
species : [],
fetched : false,
loading : false,
};
}
componentWillMount(){
this.setState({
loading : true
});
fetch('https://pokeapi.co/api/v2/pokemon?limit=20').then(res=>res.json())
.then(response=>{
this.setState({
species : response.results,
loading : true,
fetched : true
});
});
}
render(){
const {fetched, loading, species} = this.state;
let content ;
if(fetched){
content = <div className="pokemon--species--list">{
species.map((pokemon,index) => <Pokemon key={pokemon.name} id={index+1} pokemon={pokemon}/>)
}
</div>;
}else if(loading && !fetched){
content = <p> Loading ...</p>;
}
else{
content = <div/>;
}
return <div className="container">
{content}
</div>;
}
}
export default App;
I know, there is much to do there, but first I want to understand how to pass ID to pokemondetails class.
Have a look at react-router and how to pass parameters to components associated with routes. Basically, you could have two routes that would render following components PokemonList and PokemonDetail. Redirect user from the PokemonList to PokemonDetail and append pokemonId to the url ( e.g "/details/23").
After redirection 'PokemonDetail' component would be rendered and pokemonId would be available in the component.
const App = () => (
<Router>
<div>
...
<Route exact path="/" component={PokemonList}/>
<Route path="/details/:pokemonId" component={PokemonDetail}/>
</div>
</Router>
)
// access pokemonId
class PokemonDetail extends Component{
render() {
return (
<div>
<h2>{this.props.params.pokemonId}</h2>
</div>
)
}
}

Pagination gets rendered for all views but it should be rendered only on home page in ReactJS?

I have created 3 components:
content
matchinfo
layout
pagination
Whenever I click on view stats button matchinfo component view is rendered which displays matchinfo of particular match. Whenever I click on view stats button (see screenshot) it also renders pagination component also which should not be rendered how can I fix this.
Component matchinfo is child component of content component.
content.js :
import React, { Component } from 'react';
import Matchinfo from './matchinfo';
import './content.css';
class Content extends Component {
constructor(props){
super(props);
this.state = {
matches:[],
loading:true,
callmatchinfo: false,
matchid:''
};
}
componentDidMount(){
fetch('api/matches')
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
matches:res,
loading:false
});
})
}
viewstats(matchid){
this.setState({
callmatchinfo: true,
matchid: matchid
});
}
rendermatchinfo(){
return <Matchinfo matchid={this.state.matchid} />
}
renderMatches() {
return this.state.matches.slice(this.props.start, this.props.end).map(match => {
return (
<div className="col-lg-3">
<div id="content">
<p className="match">MATCH {match.id}</p>
<h4>{match.team1}</h4>
<p>VS</p>
<h4>{match.team2}</h4>
<div className="winner">
<h3>WINNER</h3>
<h4>{match.winner}</h4>
</div>
<div className="stats">
<button type="button" onClick= {()=>{this.viewstats(match.id)}} className="btn btn-success">View Stats</button>
</div>
</div>
</div>
);
})
}
render() {
if (this.state.loading) {
return <div>Loading...</div>
}
else if(this.state.callmatchinfo){
return <Matchinfo match_id={this.state.matchid} />
}
return (
<div>
<div className="row">
{this.renderMatches()}
</div>
<div className="row">
{this.state.callmatchinfo ? this.rendermatchinfo() : ''}
</div>
</div>
);
}
}
export default Content;
matchinfo.js :
import React, { Component } from 'react';
class Matchinfo extends Component {
constructor(props){
super(props);
this.state = {
info:[],
loading:true
};
}
componentWillMount(){
fetch(`api/match/${this.props.match_id}`)
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
info:res,
loading:false
})
})
}
render() {
if (this.state.loading) {
return <div>Loading...</div>
}
const {info} = this.state;
return (
<div>
<p className="match">MATCH {info.id}</p>
<h4>{info.team1}</h4>
<p>VS</p>
<h4>{info.team2}</h4>
<div className="winner">
<h3>WINNER</h3>
<h4>{info.winner}</h4>
</div>
</div>
);
}
}
export default Matchinfo;
pagination.js :
import React, { Component } from 'react';
class Pagination extends Component {
handleClick(val){
this.setState({
end:val*16,
start:end-16
});
const end = val*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<div className="container">
<ul className="pagination">
<li><a href="#" onClick={this.handleClick.bind(this, 1)}>1</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 2)}>2</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 3)}>3</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 4)}>4</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 5)}>5</a></li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
layout.js :
import React, { Component } from 'react';
import Pagination from './pagination';
import Content from './content';
class Layout extends Component {
constructor(props){
super(props);
this.state = {
start:0,
end:16
};
}
onChangePagination = (start, end) => {
this.setState({
start,
end
});
};
render() {
const {start, end} = this.state;
return (
<div>
<Content start={start} end={end}/>
<Pagination onChange={this.onChangePagination}/>
</div>
);
}
}
export default Layout;
Screenshots:
Onclick view stats button it still shows pagination:
You should approach toggling pagination the same way you did for showing match information. Hold a variable in this.state for your Layout component and make a method that will control that this.state variable. Pass that method down to your child component. Here is a barebones example:
class Layout extends Component {
constructor(props){
super(props);
this.state = {
showPagination: true
}
}
onChangePagination = () => {
this.setState({showPagination: !this.state.showPagination}) //toggles pagination
};
render() {
return (
<div>
{
this.state.showPagination
?
<Pagination onChangePagination={this.onChangePagination}/>
:
<button onClick={this.onChangePagination}>
show pagination
</button>
}
</div>
)
}
}
class Pagination extends Component {
handleClick() {
this.props.onChangePagination()
}
render() {
return (
<div>
<button onClick={this.handleClick}>
toggle pagination
</button>
</div>
)
}
}

Resources