Reactjs and Superagent pt3 - reactjs

I'm making progress on this app. I'm able to access and render the list of ingredients now I need to do the same with the name of the recipe. Postman indicates that it is under recipes.body.matches[0].sourceDisplayName. I created another function, similar to what got me the ingredients. Getting the following error...
TypeError: Cannot read property 'map' of undefined
import React from 'react';
import Request from 'superagent';
import _ from 'lodash';
export class Yum extends React.Component {
constructor(){
super();
this.state = {
searchQuery: 'onion',
recipe: {
ingredients: []
}
};
this.search = this.search.bind(this);
this.queryUpdate = this.queryUpdate.bind(this);
}
componentWillMount(){
this.search(this.state.searchQuery);
}
render(){
//const title = 'Onion Soup'; // Get this from somwhere else ?
const {recipe, searchQuery} = this.state; // Get state properties
const displayName = _.get(recipe, 'sourceDisplayName').map((sourceDisplayName) => {
return (<h4>{displayName}</h4>)
});
const listItems = _.get(recipe, 'ingredients', []).map((ingredient, sourceDisplayName) => {
return (<h5>{ingredient}</h5>);
});
return(
<div>
<input onChange={this.queryUpdate} type="text" value={searchQuery} />
<h4>{displayName}</h4>
<ul>
<li>{listItems}</li>
</ul>
</div>
)
}
queryUpdate(event) {
const searchQuery = event.target.value; // Get new value from DOM event
this.setState({searchQuery}); // Save to state
this.search(searchQuery); // Search
}
search(searchQuery) {
const url = `http://api.yummly.com/v1/api/recipes?_app_id=5129dd16&_app_key=9772f1db10ba433223ad4e765dc2b537&q=${searchQuery}&maxResult=1`
Request.get(url).then((response) => {
this.setState({
recipe: response.body.matches[0]
});
});
}
}
export default Yum;
Any suggestions?

Related

ReactJS -- Unable to find latest title from an api causing error

I have a Landing component and a NewsLatest component. I am hitting on an api and trying to find the article with the latest timestamp but iam unable to get it done in reactJS.I checked the js code its working fine but in react it is not rendering. Kindly suggest something.
import React, { Component } from 'react'
import NewsSearch from '../NewsSearch/NewsSearch';
import NewsLatest from '../NewsLatest/NewsLatest';
import './Landing.css';
import axios from 'axios';
class Landing extends Component {
state={
newsList: []
}
componentDidMount(){
axios.get(`https://api.nytimes.com/svc/topstories/v2/home.json?api-key=7cK9FpOnC3zgoboP2CPGR3FcznEaYCJv`)
.then(res=> {
this.setState({newsList: res.data.results});
});
}
render() {
// console.log(this.state.newsList);
return (
<div className="landing text-center text-white">
<h1>News Portal</h1>
<div className="news-search">
<NewsSearch />
</div>
<div className="news-latest">
<NewsLatest newsList={this.state.newsList}/>
</div>
</div>
)
}
}
export default Landing;
import React, { Component } from 'react';
// import PropTypes from 'prop-types';
class NewsLatest extends Component {
constructor(props){
super(props);
this.state = {
newsTitle:'',
abstract:'',
newsUrl:'',
}
// this.newsLatest = this.newsLatest.bind(this);
}
newsLatest = (e)=>{
// e.preventDefault();
const {newsList} = this.props;
let maxTime = newsList.map(function(o) {
return new Date(o.updated_date);
});
let maximumValue = Math.max(...maxTime);
let latestnews = newsList.filter(function (el) {
return maximumValue === new Date(el.updated_date).getTime();
})[0];
if(latestnews){
this.setState({newsTitle: latestnews.title});
return (<h4>{this.state.newsTitle}</h4>);
}
}
newsTitle = () => (
this.props.newsList.map(item => (<h2 key={item.title}>{item.title}</h2>))
)
render() {
console.log(this.props.newsList);
return (
<div>
<h2>News Latest....</h2>
{this.newsLatest()}
</div>
);
}
}
export default NewsLatest;
There is some issue in rendering in NewsLatest component. KIndly suggest something.
Try this:
You must probably be getting a maximum depth error, use a lifecycle method instead like componentDidUpdate. Update your component state only if the previous props are different from the newer ones.
Read more here: https://reactjs.org/docs/react-component.html
import React, { Component } from "react";
// import PropTypes from 'prop-types';
class NewsLatest extends Component {
constructor(props) {
super(props);
this.state = {
newsTitle: "",
abstract: "",
newsUrl: ""
};
// this.newsLatest = this.newsLatest.bind(this);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.newsList !== this.props.newsList) {
const { newsList } = this.props;
let maxTime = newsList.map(function(o) {
return new Date(o.updated_date);
});
let maximumValue = Math.max(...maxTime);
let latestnews = newsList.filter(function(el) {
return maximumValue === new Date(el.updated_date).getTime();
})[0];
this.setState({ newsTitle: latestnews.title });
}
}
// newsLatest = e => {
// // e.preventDefault();
// const { newsList } = this.props;
// let maxTime = newsList.map(function(o) {
// return new Date(o.updated_date);
// });
// let maximumValue = Math.max(...maxTime);
// let latestnews = newsList.filter(function(el) {
// return maximumValue === new Date(el.updated_date).getTime();
// })[0];
// console.log(latestnews)
// if (latestnews && latestnews.hasOwnProperty('length') && latestnews.length>0) {
// return <h4>{this.state.newsTitle}</h4>;
// }
// };
newsTitle = () =>
this.props.newsList.map(item => <h2 key={item.title}>{item.title}</h2>);
render() {
console.log(this.props.newsList);
return (
<div>
<h2>News Latest....</h2>
<h4>{this.state.newsTitle}</h4>
</div>
);
}
}
export default NewsLatest;
Also, a sandbox: https://codesandbox.io/s/hungry-frog-z37y0?fontsize=14

Undefined props in componentDidMount

This is starting to get really frustrating. Basically, I cannot access props in my subcomponents. if I try to render them directly using this.props- it works, but if I need to do additional processes with them, or save them into state, I get undefined props all the time. I have a parent component, which looks something like this:
import React from 'react';
import Title from './EventSubComponents/Title';
import SessionInfo from './EventSubComponents/SessionInfo';
import SessionTime from './EventSubComponents/SessionTime';
import Location from './EventSubComponents/Location';
import Subscribers from './EventSubComponents/Subscribers';
class EventNode extends React.Component {
constructor(props) {
super(props);
this.state = {
'event': [],
}
}
componentDidMount() {
this.getEvent(this.props.location.selectedEventId);
}
getEvent(eventId) {
fetch('/api/v.1.0/event/' + eventId, {mode: 'no-cors'})
.then(function(response) {
if(!response.ok) {
console.log('Failed to get single event.');
return;
}
return response.json();
})
.then((data) => {
if (!data) {
return;
}
this.setState({
'event': data
})
});
}
render() {
return(
<div className="event-wrapper">
<Title
title = { this.state.event.title }
date = { this.state.event.start }
/>
<SessionInfo
distance = { this.state.event.distance }
type = { this.state.event.type }
/>
<SessionTime
start = { this.state.event.start }
end = { this.state.event.end }
/>
<Location location = { this.state.event.start_location }/>
<Subscribers
subscribers = { this.state.event.subscribers }
eventId = { this.state.event._id }
/>
</div>
);
}
}
export default EventNode;
And my sub-component SessionTime, which looks like this:
import React from 'react';
import moment from 'moment';
class Title extends React.Component {
constructor(props) {
super(props);
this.state = {
'title': '',
'date': '',
}
}
componentDidMount() {
console.log(this.props.title);
console.log(this.props.date);
// undefined both props.
this.convertToTitleDate(this.props.date);
this.setState({
'title': this.props.title
})
}
convertToTitleDate(date) {
var newDate = moment(date).format('dddd, Do MMMM')
this.setState({
'date': newDate,
});
}
render() {
return (
<div className="event-title-wrapper">
<h1> { this.state.title } </h1>
<div className="event-title-date"> { this.state.date } </div>
</div>
);
}
}
export default Title;
Could anyone explain, why both this.props.date and this.props.title are undefined in my componentDidMount function? I have couple more components in my EventNode and I have the same problems in them as well.
Changing componentDidMount to componentWillMount does not help. I am fairly certain I have problems in my parent EventNode component, but I cannot figure out where. Inside EventNode render() all the state variables are defined.
You initialize event to an empty array and pass down this.state.event.start and this.state.event.end to SessionTime, which will both be undefined on first render since event has not been loaded yet and there are no start and end properties on the array.
You could instead e.g. set event to null initially, and return null from the render method until the event has been loaded.
Example
class EventNode extends React.Component {
state = {
event: null
};
// ...
render() {
const { event } = this.state;
if (event === null) {
return null;
}
return (
<div className="event-wrapper">
<Title title={event.title} date={event.start} />
<SessionInfo distance={event.distance} type={event.type} />
<SessionTime start={event.start} end={event.end} />
<Location location={event.start_location} />
<Subscribers
subscribers={event.subscribers}
eventId={this.state.event._id}
/>
</div>
);
}
}

Unable to read params property from query string?

I have a simple app that access the opentable api (http://opentable.herokuapp.com/api/restaurants). My app, when loaded, simply displays content specified from the query parameters. For example, appending ?city=toronto would give me all restaurants in Toronto. Here is a working, hardcoded example:
import React, { Component } from "react";
import Spinner from "./components/common/Spinner";
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoading: false
};
}
componentDidMount() {
// // let city = this.props.match.params.city;
// // console.log(city);
// console.log(this.props.match.params.city);
fetch("http://opentable.herokuapp.com/api/restaurants?city=Toronto")
.then(res => res.json())
.then(json => {
this.setState({
isLoading: true,
items: json
});
});
}
render() {
const { isLoading, items } = this.state;
let itemsToArray = Object.values(items);
return !isLoading ? (
<div>
<Spinner />
</div>
) : (
<div className="App">
<ul>
{itemsToArray[3].map(item => (
<div>
<li key={item.id}>{item.name}</li>
</div>
))}
</ul>
</div>
);
}
}
export default App;
If I were to uncomment console.log(this.props.match.params.city);, it tosses an error TypeError: Cannot read property 'params' of undefined. Am I accessing the params incorrectly? I'd like to do something like,
componentDidMount() {
let city = this.props.match.params.city;
fetch(`http://opentable.herokuapp.com/api/restaurants?city=${city}`)
.then(...
If you are trying to use something like:
http://myapp/page?city=Toronto
Then, this.props.match.params.city won't work. The reason being, the use-case of match.params.city is supposed to be in the Routes.
import { Route } from "react-router-dom";
<Route path="/path/:city" component={App} />
In your componentDidMount() lifecycle method, try using:
const urlParams = new URLSearchParams(window.location.search);
let city = urlParams.get('city');
For the above code, have a look at How can I get query string values in JavaScript? In your code, if you try logging the value of city, it might be undefined if you haven't configured your route this way.
Sample Code
class App extends React.Component {
state = {
city: "None"
};
componentDidMount() {
const urlParams = new URLSearchParams(window.location.search);
let city = urlParams.get("city");
this.setState({
city
});
console.log(city);
}
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<h3>You are in {this.state.city}!</h3>
</div>
);
}
}
Working Demo: CodeSandbox
You can use this function to access the URL params
var getParams = function (url) {
var params = {};
var parser = document.createElement('a');
parser.href = url;
var query = parser.search.substring(1);
var vars = query.split('&');
if(vars == ''){
params = '';
return params;
}
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
params[pair[0]] = decodeURIComponent(pair[1]);
}
return params;
};
and call it
console.log(getParams(window.location.href));
What if you try to wrap your App Class component with withRouter? so, it will look like the following:
import React, { Component } from "react";
import { withRouter } from 'react-router-dom';
import Spinner from "./components/common/Spinner";
class App extends Component {
//....
}
export default withRouter(App);

TODO project is not woking properly

Components ->
Box
Todolist
Add
AddModal
Main component App
But it is not working that is when I add a new task. It does not get added properly.
I think I cannot use this.setstate twice in a function.
Hope I am correct
Here is given the main component.
App.js :
import React, { Component } from 'react';
import './App.css';
import Box from './Components/Box';
import Add from './Components/Add';
import Todolist from './Components/Todolist';
class App extends Component {
constructor(props) {
super(props);
this.state = {
lists: '',
inputValue: '',
itemArray: []
}
}
onAddTask = () => {
this.setState ({
lists: this.state.inputValue
});
const item = this.state.itemArray;
const title = this.state.lists;
item.push({ title })
this.setState(prevState => ({
itemArray: [...prevState.lists, title]
}))
}
updateInputValue = (event) => {
this.setState({
inputValue: event.target.value
});
}
render() {
let length = this.state.itemArray.length;
return (
<div className="App">
<Box createTodo = {
<div>
{this.state.itemArray.map((itemArr) => {
return (
<div className="box">
<Todolist tasks = {itemArr} />
</div>
)
})}
</div>
}>
</Box>
<Add addTask = {this.onAddTask} inputValues = {this.updateInputValue} inputV = {this.state.inputValue} />
</div>
);
}
}
export default App;
Your addTasks function is not correct, you are mixing up things here.
In your inputValue you save the current value from the input field right? So if you write the following
this.setState({
lists: this.state.inputValue
});
you set your todo list to this single value. And your todo list is not an array anymore.
Secondly, state is imutable. So if you write the following
this.state.itemArray.push({ title });
the state will not be updated. What you actually want is the following:
onAddTask = () => {
this.setState({
itemArray: [...this.state.itemArray, this.state.inputValue]
})
}
And I'm not sure what the lists property on the state is for. You don't use it anywhere besides in your onAddTask function. So I guess you can remove it.

React dynamic search/filter returns undefined

I'm trying to create a dynamic search that can be sorted by title or date. Right now my code is focusing on title, and once I have that working I will duplicate it to make the date work too. However, right now, my filter method is not working and I don't know how to fix it without breaking my map function.
I am currently getting "Cannot read property 'toLowerCase' of undefined". It is referring to the newsItem.title, but I'm not sure how to fix it.
import React, { Component } from 'react';
import labNewsJson from '../json/labNews.json';
import '../styles/News.css';
var moment = require('moment');
const newsList = labNewsJson['news'];
class News extends Component {
constructor() {
super();
this.state = {
search: ''
};
}
updateSearch(event) {
this.setState({ search: event.target.value })
}
render() {
// let filteredNews = this.props.newsList;
newsList.sort(function(a, b) {
var dateA = new Date(a.date), dateB = new Date(b.date);
return dateB - dateA;
});
let filteredNews = newsList.filter((newsItem) => {
return newsItem.title.toLowerCase().includes(this.state.search.toLowerCase())
}
);
const news = filteredNews.map((newsItem, index) => {
return <div className='newsContainer' key={index}><h3>{newsItem.title}</h3><p><strong>{newsItem.description}</strong><br></br>{moment(newsItem.date).format("LL")}</p></div>
});
return (
<div className='container'>
<div className='pageTitle'><h1>Lab News</h1></div>
<div>{news}</div>
<input type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} />
</div>
);
}
}
export default News;

Resources