Display response object from GET request on backend in react component - node.js

I am still figuring React out and have a question. I want to display some data that I am getting back from my mLab database. When I make the request in Postman to test request i get back the object full of data and now I want to display that data in my component.
Backend/server.js:
//this is tested and works in postman
app.get('/logs', function(req, res) {
user: req.user;
res.json();
});
React action:
export const GET_DATA_SUCCESS = 'GET_DATA_SUCCESS';
export const GET_DATA_TRIGGERED = 'GET_DATA_TRIGGERED';
export const GET_DATA_FAILURE = 'GET_DATA_FAILURE';
export function getData() {
const promise = fetch('http://localhost:8080/logs');
return {
onRequest: GET_DATA_TRIGGERED,
onSuccess: GET_DATA_SUCCESS,
onFailure: GET_DATA_FAILURE,
promise,
};
}
Component where I want to display:
import React from 'react';
import {Router, Route, Link, Redirect, withRouter} from 'react-router-dom';
import { getData } from '../actions/memory';
import { connect } from 'react-redux';
export class oldMemory extends React.Component {
oldSearch(e) {
e.preventDefault();
this.props.getData();
}
render() {
return(
<div className="old-info">
<Link to="/main"><h3 className="title-journey">Travel Journal</h3></Link>
<h4>Retrieve a Memory</h4>
<p className="get-info">Look back on an old place you have visited and
reminisce.</p>
<input className="search" type="text" name="search" placeholder="Search"
/>
<button onClick={this.oldSearch.bind(this)}>Get</button>
// would like data to show up here
</div>
)
}
}
export default connect(null, { getData })(oldMemory)

I would use a state to store the data and set the state after the getData promise is resolved. Then, in the render method, i map the state data to div elements and display them in the the component.
// I assume your result of get Data is an array of
// objects {id: number,date: string, memory: string}
// and that getData is a promise
class OldMemory extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
oldSearch = (e) => {
e.preventDefault();
this.props.getData().then(data => {
// if data is null, or undefined set it to an empty array
this.setState({ data: data || [] });
})
}
render() {
// build data to div elements for display
const memories = this.state.data.map(d => <div>{d.date} - {d.memory}</div>)
return(
<div className="old-info">
<Link to="/main"><h3 className="title-journey">Travel Journal</h3></Link>
<h4>Retrieve a Memory</h4>
<p className="get-info">Look back on an old place you have visited and
reminisce.</p>
<input className="search" type="text" name="search" placeholder="Search"
/>
<button onClick={this.oldSearch}>Get</button>
// would like data to show up here
<div id="display-data">
{ memories }
</div>
</div>
</div>
);
}
}
export default connect(null, { getData })(OldMemory)

Related

I am trying to display data from an external API, how can I fix the issue in my code?

This is the error message I am getting
TypeError: undefined is not an object (evaluating 'drinkList.drinks[0]')
How can I fix the error so that I can use the app to fetch data from the external api?
This is my drink.js code:
import React, { useEffect, useState } from "react";
import axios from "axios";
import Drinks from "./Drinks";
function Home() {
const [drinkName, setDrinkName]= useState([]);
const [drinkList, setDrinkList] = useState([]);
const drinksURL = `https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${drinkName}`;
const handleChangeDrink= e => {
setDrinkName(e.target.value);
}
const getDrink = () => {
axios
.get(drinksURL)
.then(function (response) {
setDrinkList(response.data);
console.log(drinksURL);
})
.catch(function (error) {
console.warn(error);
});
};
return (
<main className="App">
<section className="drinks-section">
<input
type="text"
placeholder="Name of drink (e.g. margarita)"
onChange={handleChangeDrink}
/>
<button onClick={getDrink}>Get a Drink Recipe</button>
<Drinks drinkList={drinkList} />
</section>
</main>
);
}
export default Home;
This is my Drink.js code:
import React from "react";
function Drinks({ drinkList }) {
if (!drinkList) return <></>;
return (
<section className="drinkCard">
<h1>{drinkList.drinks[0].strDrink}</h1>
</section>
);
}
export default Drinks;
Couple of problems here...
drinkName is initialised as an array but it appears to be expecting a string
drinkList is initialised as an array but the data from the API is an object. You may want to assign the drinks array from the response instead
drinksUrl is never updated
An empty array is still truthy
Some easy fixes
const [drinkName, setDrinkName]= useState(null) // initialise as null
const [drinkList, setDrinkList] = useState([])
// don't include the "s" parameter here
const drinksURL = "https://www.thecocktaildb.com/api/json/v1/1/search.php"
const getDrink = () => {
// pass drinkName as a param
axios.get(drinksURL, {
params: { s: drinkName }
}).then(res => {
// note that we're extracting `drinks`
setDrinkList(res.data.drinks)
}).catch(console.warn)
}
and in Drink.js
function Drinks({ drinkList }) {
// check the array length
return drinkList.length && (
<section className="drinkCard">
<h1>{drinkList[0].strDrink}</h1>
</section>
)
}

How can I take a value from an input tag in a TSX component, and use that value in a Node JS file in a different directory?

I'm working on a personal project where I'm pulling an API through Fetch; at the moment I can send the call from my index.js file to a TSX component that calls the API URL when my SearchButton component is clicked, but the search term needs to be declared in index.js.
Here's my SearchButton code (TSX):
import React, { useState } from 'react'
function SearchButton() {
const [ newsResponse, setNewsResponse ]= useState(null);
function queryOnClick() {
fetch(`http://localhost:4000/news-api`, {
headers: { 'Content-Type': 'application/json' }
})
.then((response) => response.json())
.then((result) => {
console.log('result:', result);
setNewsResponse(result);
})
.catch((ex) => {
console.log('error:', ex);
});
}
return (
<div className="theme--white">
<button className="search__button padding-1 margin-1 margin-left-6" onClick={queryOnClick}>
Click to search
</button>
{newsResponse && newsResponse.articles ? (
<div className="results__container padding-2 theme--mist">
{newsResponse.articles.map((article: {
title: React.ReactNode;
author: string;
content: string;
url: string;
}) => (
<div className="article__container box-shadow padding-2 margin-4 margin-left-6 margin-right-6 theme--white">
<h2 className="article__title padding-bottom-2 margin-bottom-2">{article.title}</h2>
<h3 className="article__author padding-bottom-2 margin-bottom-2">Written by: {article.author || 'An uncredited author'}</h3>
<p className="article__content">
{article.content.length > 150 ?
`${article.content.substring(0, 150)}... [Article shortened - Click the URL below to read more]` : article.content
}
</p>
<div className="article__url margin-top-2">
<p>
<p>Source:</p>
<a href={article.url}>{article.url}</a>
</p>
</div>
</div>
))}
</div>
) : null}
</div>
);
}
export default SearchButton;
I want to change that so a user can search for an article from the API by using a HTML input to submit a topic which would amend the API URL. For instance, if I searched Bitcoin, it would search https://API-${Bitcoin}.com. Due to CORS policy blocking, I can't just call the API in my TSX file as it has to go from localhost:3000 > localhost:4000 via the Node JS file.
At the moment, my input renders the user's query into the console, but I can't seem to get it over to my index.js file. How can I pass a value that's either in the console.log, or from the input's value, through to my Node JS index.js file?
Here's my SearchBar file that handles my Input (TSX):
import React, { Component } from 'react';
type SearchBarProps = {
searchNews: (text: string) => void;
}
type SearchBarState = {
searchString: string;
}
class SearchBar extends Component<SearchBarProps, SearchBarState> {
static defaultProps = {
searchNews: (text: string) => {}
}
state = {
searchString: ''
}
searchNews = (e: any) => {
const { searchString } = this.state
if(e.key === 'Enter' && searchString !== '') {
e.preventDefault();
e.stopPropagation();
this.props.searchNews(searchString)
console.log(searchString)
}
}
onSearchTextChange = (e: any) => {
this.setState({
searchString: e.target.value.trim()
})
}
render() {
return (
<div>
<form>
<div>
<input
id="search"
type="search"
value={this.state.searchString}
onChange={this.onSearchTextChange}
onKeyPress={e => this.searchNews(e)} placeholder="Search" />
</div>
</form>
</div>
);
}
}
export default SearchBar;
...And here's my index.js Node JS file (JS):
/*
* Libs
*/
const express = require('express');
const fetch = require('node-fetch');
const cors = require('cors');
const app = express();
/*
* Constants
*/
const PORT = 4000;
const API_KEY = 'x';
const SEARCH_QUERY = "Bitcoin";
const SORT_BY = "popularity";
const PAGE_SIZE = 10;
/*
* Setup CORS - This is needed to bypass NewsAPI CORS Policy Blocking by rerouting request to localhost
*/
const corsOptions = {
origin: 'http://localhost:3000',
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));
/*
* Setup to request NewsAPI data using Fetch API
*/
app.get('/news-api', function (req, res) {
fetch(`https://newsapi.org/v2/everything?q=${SEARCH_QUERY}&sortBy=${SORT_BY}&pageSize=${PAGE_SIZE}&apiKey=${API_KEY}`, {
headers: { 'Content-Type': 'application/json' }
})
.then((response) => response.json())
.then((result) => {
console.log('result:', result);
res.json(result);
})
.catch((ex) => {
console.log('error:', ex);
res.status(400).send({
message: 'This is an error!',
error: ex
});
});
})
/*
* Start Backend API Proxy server
*/
app.listen(PORT, () => {
console.log(`=================`)
console.log(`API Connected!`)
console.log(`Listening at http://localhost:${PORT}`)
console.log(`=================`)
})
TLDR:
I have a TSX component that is an input (A - value={this.state.searchString}).
I want that input's value to go to a Node JS file to append a URL via a const (B - const SEARCH_QUERY).
I know what to pull from A, and where to put it in B, but don't know how to do so.
Full tech stack
Using Fetch API, React, TypeScript, Node JS and Webpack.
File paths
SearchButton: project/frontend/src/components/SearchButton/SearchButton.tsx
SearchBar: project/frontend/src/components/SearchBar/SearchBar.tsx
Node JS handler: project/backend/index.js
Essentially what you are asking here is how to pass data from the frontend to the backend. The way to do this is by including the user's search term in your fetch request to the backend. You can either include it in the body of a POST request or include it as a query string in the URL. You would need to use the body for passing large amounts of data, but something as simple as a search term can be done with a query string.
Front End
Include the current search term as a query parameter of your fetch request. I am using encodeURIComponent to apply percent-encoding to special characters.
function queryOnClick() {
// applies percent-encoding to special characters
const search = encodeURIComponent(searchString);
const url = `http://localhost:4000/news-api?search=${search}`;
fetch(url, {
...
You are missing the communication between your SearchButton and SearchBar components. I am not sure where these two components are in relation to each other on your page. If they are siblings then you will need to lift the searchString state and the queryOnClick function up to a shared parent.
I rearranged all of your components so that you have access to the right state in the right places.
import React, { useState } from "react";
function SearchButton({ onClick }: { onClick: () => void }) {
return (
<button
className="search__button padding-1 margin-1 margin-left-6"
onClick={onClick}
>
Click to search
</button>
);
}
interface SearchBarProps {
searchNews: () => void;
searchString: string;
setSearchString: (s: string) => void;
}
function SearchBar({ searchNews, searchString, setSearchString }: SearchBarProps) {
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && searchString !== "") {
e.preventDefault();
e.stopPropagation();
searchNews();
}
};
const onSearchTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchString(e.target.value.trim());
};
return (
<div>
<form>
<div>
<input
id="search"
type="search"
value={searchString}
onChange={onSearchTextChange}
onKeyPress={handleKeyPress}
placeholder="Search"
/>
</div>
</form>
</div>
);
}
interface Article {
title: string;
author: string;
content: string;
url: string;
}
interface NewsResponse {
articles: Article[];
}
function ArticleList({ articles }: NewsResponse) {
return (
<div className="results__container padding-2 theme--mist">
{articles.map((article) => (
<div className="article__container box-shadow padding-2 margin-4 margin-left-6 margin-right-6 theme--white">
<h2 className="article__title padding-bottom-2 margin-bottom-2">
{article.title}
</h2>
<h3 className="article__author padding-bottom-2 margin-bottom-2">
Written by: {article.author || "An uncredited author"}
</h3>
<p className="article__content">
{article.content.length > 150
? `${article.content.substring(
0,
150
)}... [Article shortened - Click the URL below to read more]`
: article.content}
</p>
<div className="article__url margin-top-2">
<p>
<p>Source:</p>
<a href={article.url}>{article.url}</a>
</p>
</div>
</div>
))}
</div>
);
}
function SearchPage() {
const [newsResponse, setNewsResponse] = useState<NewsResponse | null>(null);
const [searchString, setSearchString] = useState("");
function queryOnClick() {
// applies percent-encoding to special characters
const search = encodeURIComponent(searchString);
const url = `http://localhost:4000/news-api?search=${search}`;
fetch(url, {
headers: { "Content-Type": "application/json" }
})
.then((response) => response.json())
.then((result) => {
console.log("result:", result);
setNewsResponse(result);
})
.catch((ex) => {
console.log("error:", ex);
});
}
return (
<div className="theme--white">
<SearchBar
searchNews={queryOnClick}
searchString={searchString}
setSearchString={setSearchString}
/>
<SearchButton onClick={queryOnClick} />
{newsResponse && newsResponse.articles ? (
<ArticleList articles={newsResponse.articles} />
) : null}
</div>
);
}
export default SearchPage;
Back End
You need to access the search term from the search parameter of the request URL. We use the req.params property to get a dictionary of params. We can use your previous search term "Bitcoin" as the default value if there was no search param on the request.
I'm not certain if we need to encode again here or not -- you'll want to play with that.
app.get('/news-api', function (req, res) {
const searchQuery = req.params.search || "Bitcoin";
fetch(`https://newsapi.org/v2/everything?q=${searchQuery}&sortBy=${SORT_BY}&pageSize=${PAGE_SIZE}&apiKey=${API_KEY}`, {
...

how to solve this no-unused-vars error in NodeJS?

I am creating a todo app using MERN stack.I am new to MERN stack technology and I kindly neeed your help solving this error.I have provided the code for my app.js file and todo.js file.I can't clearly find the solution of this error anywhere on the internet.
I am getting this error while runnng the node js app using npm start command.
Compiled with warnings.
src\App.js
Line 4:8: 'Todo' is defined but never used no-unused-vars
Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.
App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Todo from './components/Todo.js';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
Todo.js
import React, { Component } from 'react'
import axios from 'axios';
// eslint-disable-next-line
export class Todo extends Component {
constructor(props) {
super(props)
this.state = {
todos : [],
item : ""
}
}
changeHandler = (event) => {
this.setState({item: event.target.value})
}
clickHandler = (event) => {
event.preventDefault()
console.log(this.state.item)
axios({
method: 'post',
url: 'http://localhost:3000/',
data: {
todo: this.state.item,
}
});
this.setState({item:''})
}
componentDidMount() {
axios.get('http://localhost:3000/').then((response) => {
console.log(response.data.data)
let data = [];
console.log(response.data.data.length)
for(var i =0; i < response.data.data.length; i++){
data.push(response.data.data[i].todo)
}
this.setState({todos: data})
});
}
componentDidUpdate() {
axios.get('http://localhost:3000/').then((response) => {
console.log(response.data.data)
let data = [];
console.log(response.data.data.length)
for(var i =0; i < response.data.data.length; i++){
data.push(response.data.data[i].todo)
}
this.setState({todos: data})
});
}
render() {
return (
<div>
<input type="text" onChange={this.changeHandler}/>
<button type="submit" onClick={this.clickHandler}>add</button>
<div>
<ul>{this.state.todos.map((todo, index) => <li key={index}>{todo}</li>)}</ul>
</div>
</div>
)
}
}
export default Todo
That warning you are getting because even though you are importing Todo file in your App.js file but you aren't using it anywhere.Either try using it in App.js or remove the import(in case you don't need it).That should fix the warning.
Or add // eslint-disable-next-line just before the import Todo.. statement in App.js

React: How to update the DOM with API results

My goal is to take the response from the Google API perspective and display the value into a div within the DOM.
Following a tutorial : https://medium.com/swlh/combat-toxicity-online-with-the-perspective-api-and-react-f090f1727374
Form is completed and works. I can see my response in the console. I can even store the response into an object, array, or simply extract the values.
The issue is I am struggling to write the values to the DOM even though I have it saved..
In my class is where I handle all the API work
class App extends React.Component {
handleSubmit = comment => {
axios
.post(PERSPECTIVE_API_URL, {
comment: {
text: comment
},
languages: ["en"],
requestedAttributes: {
TOXICITY: {},
INSULT: {},
FLIRTATION: {},
THREAT: {}
}
})
.then(res => {
myResponse= res.data; //redundant
apiResponse.push(myResponse);//pushed api response into an object array
console.log(res.data); //json response
console.log(apiResponse);
PrintRes(); //save the values for the API for later use
})
.catch(() => {
// The perspective request failed, put some defensive logic here!
});
};
render() {
const {flirty,insulting,threatening,toxic}=this.props
console.log(flirty); //returns undefined, makes sense upon initialization but does not update after PrintRes()
return (
<div className="App">
<h1>Please leave a comment </h1>
<CommentForm onSubmit={this.handleSubmit} />
</div>
);
}
}
When I receive a response from the API I use my own function to store the data, for use later, the intention being to write the results into a div for my page
export const PrintRes=() =>{
// apiResponse.forEach(parseToxins);
// myResponse=JSON.stringify(myResponse);
for (var i = 0; i < apiResponse.length; i++) {
a=apiResponse[i].attributeScores.FLIRTATION.summaryScore.value;
b=apiResponse[i].attributeScores.INSULT.summaryScore.value;
c=apiResponse[i].attributeScores.THREAT.summaryScore.value;
d=apiResponse[i].attributeScores.TOXICITY.summaryScore.value;
}
console.log("hell0");//did this function run
// render(){ cant enclose the return in the render() because I get an error on the { , not sure why
return(
<section>
<div>
<p>
Your comment is:
Flirty: {flirty}
</p>
</div>
<div>
<p>
Your comment is:
insulting: {insulting}
</p>
</div>
<div>
<p>
Your comment is:
threatening: {threatening}
</p>
</div>
<div>
<p>
Your comment is:
toxic: {toxic}
</p>
</div>
</section>
);
}
Variables and imports at the top
import React from "react";
//needed to make a POST request to the API
import axios from "axios";
import CommentForm from "../components/CommentForm";
var myResponse;
var apiResponse= [];
let a,b,c,d;
let moods = {
flirty: a,
insulting:b,
threatening:c,
toxic:d
}
If I understand correctly You need to create a state where you store data from api.
States in react works like realtime stores to refresh DOM when something change. this is an example to use it
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
apiData: undefined
};
}
fetchData() {
this.setState({
apiData: "Set result"
});
}
render() {
const { apiData } = this.state;
return (
<div>
<button onClick={this.fetchData.bind(this)}>FetchData</button>
<h3>Result</h3>
<p>{apiData || "Nothing yet"}</p>
</div>
);
}
}
you can check it here: https://codesandbox.io/s/suspicious-cloud-l1m4x
For more info about states in react look at this:
https://es.reactjs.org/docs/react-component.html#setstate

pass state as props in component child in React

I get data from a local server, catch them with axios.get, and save them in my state. It's ok, but when i want to pass it as props in an component child, KABOOM! Doesn't work.
I'm looking for a solution, I think it's lyfecycle problem but i'm not sure.
App.js
import React, { Component } from 'react';
import './style/App.css';
import axios from 'axios'
import Table from './Components/Table'
class App extends Component {
state = {
tabData: [],
}
componentWillMount = () => {
this.getDataFromServer()
}
getDataFromServer = () => {
axios.get("http://localhost:8000")
.then((response) => {
const twentyObj = response.data.splice(-20);
this.setState({
tabData:twentyObj
})
console.log(this.state.tabData)
})
.catch(function (error) {
console.log(error);
})
}
render() {
return (
<div className="App">
<Table stateData={this.state.tabData}/>
</div>
);
}
}
export default App;
Developer Tools Browser say:
TypeError: _this.props is undefined
(for this.props.tabData.map in Table.js)
Table.js
import React from 'react';
import Cell from './Cell'
const Table = (props) => {
return(
<div>
{this.props.tabData.map( item =>
<Cell key={item.index}
time={item.timestamp}
nasdaq={item.stocks.NASDAQ}
cac40={item.stocks.CAC40}/>
)}
</div>
)
}
export default Table;
Table is functional component this has no value there and thats what the error message is telling you.
You should use props.tabData and not this.props.tabData
UPDATE:
Here you are passing it as stateData and not tabData Try props.stateData

Resources