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

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..."

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.

How to display all properties of an object on another component after selecting one of its object.property on react?

I am creating an app where I have an employee object stored in localhost://8081/employee . The object has 5 properties- empid (string), name, email, mobile and age. My employee-list page is currently displaying a list of empid and name and when a name is clicked, using router it should open employee-details component below it which would display all 5 properties of the selected employee at the bottom of the page. The router and the list name and empid list work but the expansion on click is not displaying the right data and I'm wondering how to fix it. When I console.log this.target.value, I am always getting 0 though I should be getting the selected employee's data. How can I pass the data to the employee-details component and display the whole object in employee-details component? Any guidance would be greatly appreciated, like a lot, thank you!
employee-list.js
import React, { Component } from 'react';
import './employees-list.css';
import axios from 'axios';
import EmployeeDetails from './employee-details/employee-details';
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
export default class EmployeesList extends Component{
constructor(props){
super(props);
this.state = {
employees : []
}
}
componentDidMount() {
this.getEmps();
}
getEmps() {
axios.get('http://localhost:8081/employee')
.then(res => {
this.setState({ employees : res.data });
console.log(res.data);
})
.catch(function (error) {
console.log("Error: " + error);
});
}
selectEmp = (e) => {
console.log(e.target.value)
}
render() {
const{ name } = this.state;
return(
<Router>
<div className="row">
<div className="col-2">
<h6 className="font-weight-bold">Employee ID</h6>
{this.state.employees.map(employee => <li>{employee.empid}</li>)}
</div>
<div className="col-2">
<h6 className="font-weight-bold">Name</h6>
<Link to="/emplist/empid">{this.state.employees.map(
employee => <li value={this.state.employees.empid} onClick={e => this.selectEmp(e, "value")}>{employee.name}</li>)}</Link>
</div>
<div className="row">
<Switch>
<Route exact path="/emplist/empid">
<EmployeeDetails/>
</Route>
</Switch>
</div>
</div>
</Router>
)
}
}
employee-details.js
import React, { Component } from 'react';
// import './employees-details.css';
import axios from 'axios';
export default class EmployeeDetails extends Component{
constructor(props){
super(props);
this.state = {
employee : []
}
}
render() {
const{ employee } = this.state;
return(
<div className="row">
<div className="col-2">
<h6 className="font-weight-bold">Employee ID</h6>
{this.state.employee.empid}
</div>
<div className="col-2">
<h6 className="font-weight-bold">Name</h6>
{this.state.employee.name}
</div>
{/* <div className="col-2">
<h6 className="font-weight-bold">Age</h6>
{this.state.employee.age}
</div>
<div className="col-3">
<h6 className="font-weight-bold">Email</h6>
{this.state.employee.email}
</div>
<div className="col-2">
<h6 className="font-weight-bold">Mobile</h6>
{this.state.employee.mobile}
</div> */}
</div>
)
}
}
In state add new property:
this.state = {
employees : [],
selectedEmpl : {}
}
Edit selectEmpl handler as:
selectEmp = (e) => {
console.log(e.target.value);
const empl = this.state.employees.filter((emp)=> emp.empid === e)[0] || {};
this.setState({ ...this.state, selectedEmpl : empl });
}
Then pass selectedEmpl as props:
<EmployeeDetails employee={this.state.selectedEmpl}/>
Few changes in EmployeeDetails component:
You are destructuring state to get employee, so there's no need to do this.state or this.props down in the render().
import React, { Component } from 'react';
// import './employees-details.css';
axios from 'axios';
export default class EmployeeDetails extends Component{
constructor(props){
super(props);
}
render() {
const { employee } = this.props;
return(
<div className="row">
<div className="col-2">
<h6 className="font-weight-bold">Employee ID</h6>
{employee.empid}
</div>
<div className="col-2">
<h6 className="font-weight-bold">Name</h6>
{employee.name}
</div>
{/* <div className="col-2">
<h6 className="font-weight-bold">Age</h6>
{employee.age}
</div>
<div className="col-3">
<h6 className="font-weight-bold">Email</h6>
{employee.email}
</div>
<div className="col-2">
<h6 className="font-weight-bold">Mobile</h6>
{employee.mobile}
</div> */}
</div>
)
}
}

Unable to pass value of props in React component

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

Onclick button I want to render multiple components in ReactJS?

I have made Matchinfo component and Navbar component. When I click on view stats button it should render Navbar(so that we can navigate back to home page and vice-versa) and Matchinfo component.
Content.js :
import React, { Component } from 'react';
import './content.css';
class Content extends Component {
constructor(props){
super(props);
this.state = {
matches:[],
loading:true
};
}
componentDidMount(){
fetch('api/matches')
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
matches:res.slice(0,16),
loading:false
})
})
}
renderMatches() {
return this.state.matches.map(match => {
return (
<div class="col-lg-3">
<div id="content">
<p class="match">MATCH {match.id}</p>
<h4>{match.team1}</h4>
<p>VS</p>
<h4>{match.team2}</h4>
<div class="winner">
<h3>WINNER</h3>
<h4>{match.winner}</h4>
</div>
<div class="stats">
Button ---> <button type="button" class="btn btn-success">View Stats</button>
</div>
</div>
</div>
);
})
}
render() {
if (this.state.loading) {
return <img src="https://upload.wikimedia.org/wikipedia/commons/b/b1/Loading_icon.gif" />
}
return (
<div>
<div class="row">
{this.renderMatches()}
</div>
</div>
);
}
}
export default Content;
On button click it should render 2 different components how can I do that ?
see below component which must be rendered :
Navbar component:
import React, { Component } from 'react';
class Navbar extends Component {
render() {
return (
<div>
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Cricket App</a>
</div>
<ul class="nav navbar-nav">
<li>Home</li>
</ul>
</div>
</nav>
</div>
);
}
}
export default Navbar;
Matchinfo component:
import React, { Component } from 'react';
class Matchinfo extends Component {
constructor(props){
super(props);
this.state = {
info:[],
loading:true
};
}
componentDidMount(){
fetch('api/match/:match_id')
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
info:res,
loading:false
})
})
}
render() {
if (this.state.loading) {
return <img src="https://upload.wikimedia.org/wikipedia/commons/b/b1/Loading_icon.gif" />
}
const {info} = this.state;
return (
<div>
<p class="match">MATCH {info.id}</p>
<h4>{info.team1}</h4>
<p>VS</p>
<h4>{info.team2}</h4>
<div class="winner">
<h3>WINNER</h3>
<h4>{info.winner}</h4>
</div>
</div>
);
}
}
export default Matchinfo;
Screenshot for more clarification see view stats button (green button):
I dont think you need to call route for MatchInfo from app.js. Check below updated code. You will see navbar in your page when you click on view stats if you my suggested code in previous post. It should work
The flow here is your content component displays view stats so within content I am calling MaTchInfo component by passing matchId as props and MatchInfo component passing that matchId to fetch api call in componentDidMount. Thats all.
import React, { Component } from 'react';
import './content.css';
import MatchInfo './components/MatchInfo'
class Content extends Component {
constructor(props){
super(props);
this.state = {
matches:[],
loading:true,
callMatchInfo: false,
matchId: ''
};
this.viwStats = this.viwStats.bind(this);
}
componentDidMount(){
fetch('api/matches')
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
matches:res.slice(0,16),
loading:false
})
})
}
viwStats(matchId){
this.setState({
callMatchInfo: true,
matchId: matchId
})
}
renderMatchInfo(){
<MatchInfo matchId={this.state.matchId} />
}
renderMatches() {
return this.state.matches.map(match => {
return (
<div class="col-lg-3">
<div id="content">
<p class="match">MATCH {match.id}</p>
<h4>{match.team1}</h4>
<p>VS</p>
<h4>{match.team2}</h4>
<div class="winner">
<h3>WINNER</h3>
<h4>{match.winner}</h4>
</div>
<div class="stats">
<button type="button" onClick={this.viwStats(match.id)} class="btn btn-success">View Stats</button>
</div>
</div>
</div>
);
})
}
render() {
if (this.state.loading) {
return <img src="https://upload.wikimedia.org/wikipedia/commons/b/b1/Loading_icon.gif" />
}
return (
<div>
<div class="row">
{this.renderMatches()}
</div>
<div class="row">
{this.state.callMatchInfo ? this.renderMatchInfo() : ''}
</div>
</div>
);
}
}
export default Content;
AND in your Matchinfo component: this.props.matchId as request param in fetch call
componentDidMount(){
fetch(`api/match/${this.props.matchId}`)
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
info:res,
loading:false
})
})
}

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